home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1995…tember: Reference Library / Dev.CD Sep 95 RL / Dev.CD Sep 95 RL.toast / mac / Technical Documentation / develop / develop Issue 6 code / TCP / NewsWatcher / NW Source / Source / message.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-07-11  |  146.2 KB  |  5,632 lines  |  [TEXT/MMCC]

  1. /*----------------------------------------------------------------------------
  2.  
  3.     message.c
  4.  
  5.     This module handles message windows.
  6.     
  7.     Copyright © 1994-1995, Northwestern University.
  8.  
  9. ----------------------------------------------------------------------------*/
  10.  
  11. #include <string.h>
  12. #include <stdio.h>
  13.  
  14. #include <Icons.h>
  15.  
  16. #include "glob.h"
  17. #include "message.h"
  18. #include "dialog.h"
  19. #include "header.h"
  20. #include "menus.h"
  21. #include "print.h"
  22. #include "strutil.h"
  23. #include "wind.h"
  24. #include "send.h"
  25. #include "newswatcher.h"
  26. #include "url.h"
  27. #include "tescroll.h"
  28. #include "drawutil.h"
  29. #include "memutil.h"
  30. #include "windutil.h"
  31. #include "teutil.h"
  32. #include "article.h"
  33. #include "fileutil.h"
  34. #include "sfutil.h"
  35. #include "prefs.h"
  36. #include "resutil.h"
  37. #include "iconutil.h"
  38. #include "listutil.h"
  39. #include "dragutil.h"
  40. #include "group.h"
  41. #include "status.h"
  42. #include "key.h"
  43. #include "ic.h"
  44. #include "help.h"
  45.  
  46.  
  47.  
  48. #define kTooManyGroupsDlg            145
  49. #define kMessageFileExistsAlert        148
  50. #define kAskSendOrSaveAlert            134
  51.  
  52. #define kSendButtonID        500            /* Rsrc id of Send button control */
  53. #define kTabCheckboxID        501            /* Rsrc id of tab stops checkbox control */
  54. #define kWrapCheckboxID        502            /* Rsrc id of wrap checkbox control */
  55.  
  56. #define kSendButtonTopNoLabels        9    /* top coord of send button if no icon labels */
  57. #define kSendButtonTopLabels        16    /* top coord of send button if icon labels */
  58.  
  59. #define kNewsIconID            200            /* Resource id of news icon family */
  60. #define kMailIconID            201            /* Resource id of mail icon family */
  61. #define kSelfIconID            202            /* Resource id of self icon family */
  62.  
  63. #define kIconV                3            /* top coord of icons */
  64. #define kNewsIconH            30            /* left coord of news icon */
  65. #define kMailIconH            110            /* left coord of mail icon */
  66. #define kSelfIconH            195            /* left coord of self icon */
  67. #define kCheckMarkV            25            /* bot coord of check mark */
  68. #define kCheckMarkDeltaH    16            /* offset from left coord of check mark to left
  69.                                            coord of icon */
  70.                                            
  71. #define kOptionsPanelLeftMargin        10    /* left margin for options panel controls */
  72.  
  73. #define kMaxFields             15            /* max number of TE fields in window */
  74.  
  75. #define kMinWindowWidth        320            /* minimum window width */
  76.  
  77. #define kQuoteStringResID    128            /* 'TEXT' resource ids for saved message files */
  78. #define kNewsgroupsResID    129
  79. #define kToResID            130
  80. #define kSubjectResID        131
  81. #define kCcResID            132
  82. #define kBccResID            133
  83. #define kReplytoResID        134
  84. #define kFollowuptoResID    135
  85. #define kKeywordsResID        136
  86. #define kDistributionResID    137
  87. #define kExtraNewsResID        138
  88. #define kExtraMailResID        139
  89. #define kSignatureResID        140
  90. #define kReferencesResID    141
  91. #define kFromResID            142
  92.  
  93.  
  94.  
  95. #pragma options align=mac68k
  96. typedef struct TMiscMessageWindowInfo {
  97.     Boolean newsIcon;            /* true if news icon checked */
  98.     Boolean mailIcon;            /* true if mail icon checked */
  99.     Boolean selfIcon;            /* true if self icon checked */
  100.     Boolean tabEnabled;            /* true if body tabs currently enabled for this window */
  101.     Boolean wrapOnSend;            /* true to wrap message before sending it */
  102.     short tabStops;                /* current tab stops for this window */
  103. } TMiscMessageWindowInfo;
  104. #pragma options align=reset
  105.  
  106.  
  107.  
  108. static WindowPtr gDragDestWindow;        /* pointer to drag destination window */
  109. static TEHandle gDestField;                /* current drag destination field */
  110. static TEHandle gFinalDestField;        /* final drag destination field */
  111. static short gDestFieldOffset;            /* current offset into drag destination field */
  112. static short gFinalDestFieldOffset;        /* final offset into final drag destination field */
  113. static Handle gDragData;                /* handle to drag data */
  114. static Boolean gDragTextCopy;            /* true if text drag is copy, false if move */
  115. static Boolean gDragInsertTextQuoted;    /* true to insert dragged text quoted */
  116.  
  117. static TEClickLoopUPP gAutoScrollUPP;
  118. static DragTrackingHandlerUPP gHandleTrackingUPP;
  119. static DragReceiveHandlerUPP gHandleReceiveUPP;
  120. static ControlActionUPP gScrollActionUPP;
  121.  
  122.  
  123.  
  124. /*----------------------------------------------------------------------------
  125.     TooManyGroupsDialog 
  126.     
  127.     Present the "too many groups" dialog.
  128.             
  129.     Entry:    n = number of groups.
  130.             
  131.     Exit:    function result = error code.
  132. ----------------------------------------------------------------------------*/
  133.  
  134. static OSErr TooManyGroupsDialog (short n)
  135. {
  136.     OSErr err = noErr;
  137.     DialogPtr dlg = nil;
  138.     Str255 str;
  139.     short item;
  140.     
  141.     err = MyGetNewDialog(kTooManyGroupsDlg, cancel, cancel, &dlg);
  142.     if (err != noErr) return err;
  143.     NumToString(n, str);
  144.     ParamText(str, "\p", "\p", "\p");
  145.     SysBeep(0);
  146.     MyModalDialog(dlg, gDialogFilterUPP, &item);
  147.     err = DoClose(dlg);
  148.     if (err != noErr) return err;
  149.     if (item == cancel) return userCanceledErr;
  150.     return noErr;
  151. }
  152.  
  153.  
  154.  
  155. /*----------------------------------------------------------------------------
  156.     GetViewRect 
  157.     
  158.     Compute the "view" rectangle of a message window
  159.             
  160.     Entry:    wind = pointer to message window.
  161.             
  162.     Exit:    *viewRect = text rectangle.
  163.     
  164.     The "view" rectangle is the content area of the window minus the
  165.     panel area and the scroll bar areas.
  166. ----------------------------------------------------------------------------*/
  167.  
  168. static void GetViewRect (WindowPtr wind, Rect *viewRect)
  169. {
  170.     TWindow **info;
  171.  
  172.     info = (TWindow**)GetWRefCon(wind);
  173.     *viewRect = wind->portRect;
  174.     viewRect->top += (**info).panelHeight;
  175.     viewRect->bottom -= 15;
  176.     viewRect->right -= 15;
  177. }
  178.  
  179.  
  180.  
  181. /*----------------------------------------------------------------------------
  182.     GetTextRect 
  183.     
  184.     Compute the "text" rectangle of a message window
  185.             
  186.     Entry:    wind = pointer to message window.
  187.             
  188.     Exit:    *textRect = text rectangle.
  189.     
  190.     The "text" rectangle is the view rectangle inset by kTextMargin on
  191.     all four sides. This is the area of the window where the text is 
  192.     displayed.
  193. ----------------------------------------------------------------------------*/
  194.  
  195. static void GetTextRect (WindowPtr wind, Rect *textRect)
  196. {
  197.     GetViewRect(wind, textRect);
  198.     InsetRect(textRect, kTextMargin, kTextMargin);
  199. }
  200.  
  201.  
  202.  
  203. /*----------------------------------------------------------------------------
  204.     InitCurField 
  205.     
  206.     Initialize the currently active field for a message window.
  207.             
  208.     Entry:    wind = pointer to message window.
  209. ----------------------------------------------------------------------------*/
  210.  
  211. static void InitCurField (WindowPtr wind)
  212. {
  213.     TWindow **info;
  214.     short i;
  215.     TMsgFieldInfo **fields, *f;
  216.     TEHandle edit;
  217.     
  218.     info = (TWindow**)GetWRefCon(wind);
  219.     fields = (**info).fields;
  220.     for (i = 0, f = *fields; ; i++, f++) {
  221.         edit = f->edit;
  222.         if ((**edit).teLength == 0) {
  223.             (**info).curField = i;
  224.             break;
  225.         }
  226.         if (edit == (**info).theTE) {
  227.             TESetSelect(0, 0, edit);
  228.             (**info).curField = i;
  229.             break;
  230.         }
  231.     }
  232.     if (gHaveTEOutlineHilite) TEFeatureFlag(teFOutlineHilite, TEBitSet, edit);
  233. }
  234.  
  235.  
  236.  
  237. /*----------------------------------------------------------------------------
  238.     FixHeight 
  239.     
  240.     Round down window height to an exact multiple of lines.
  241.             
  242.     Entry:    wind = pointer to message window.
  243.             *height = window height.
  244.             
  245.     Exit:    *height = adjusted window height
  246. ----------------------------------------------------------------------------*/
  247.  
  248. static void FixHeight (WindowPtr wind, short *height)
  249. {
  250.     TWindow **info;
  251.     short panelHeight, lineHeight, adjust;
  252.  
  253.     info = (TWindow**)GetWRefCon(wind);
  254.     panelHeight = (**info).panelHeight;
  255.     lineHeight = GetFontLineHeight(wind);
  256.     adjust = panelHeight + 15 + 2*kTextMargin;
  257.     *height = (*height - adjust) / lineHeight * lineHeight + adjust;
  258. }
  259.  
  260.  
  261.  
  262. /*----------------------------------------------------------------------------
  263.     MinHeight 
  264.     
  265.     Compute the minimum height of a message window.
  266.             
  267.     Entry:    wind = pointer to message window.
  268. ----------------------------------------------------------------------------*/
  269.  
  270. static short MinHeight (WindowPtr wind)
  271. {
  272.     TWindow **info;
  273.     short lineHeight, height, extra;
  274.     
  275.     info = (TWindow**)GetWRefCon(wind);
  276.     lineHeight = GetFontLineHeight(wind);
  277.     extra = lineHeight + 15 + 2*kTextMargin;
  278.     if (extra < 65) extra = 65 + lineHeight;
  279.     height = (**info).panelHeight + (**info).optionsPanelHeight + extra;
  280.     FixHeight(wind, &height);
  281.     return height;
  282. }
  283.  
  284.  
  285.  
  286. /*----------------------------------------------------------------------------
  287.     MinWidth 
  288.     
  289.     Compute the minimum width of a message window.
  290.             
  291.     Entry:    wind = pointer to message window.
  292. ----------------------------------------------------------------------------*/
  293.  
  294. static short MinWidth (WindowPtr wind)
  295. {
  296.     TWindow **info;
  297.     TEHandle field;
  298.     short tabFieldRight, quoteStringFieldRight;
  299.     short minWidth;
  300.     
  301.     info = (TWindow**)GetWRefCon(wind);
  302.     field = (**info).tabField;
  303.     tabFieldRight = (**field).viewRect.right;
  304.     field = (**info).quoteStringField;
  305.     quoteStringFieldRight = (**field).viewRect.right;
  306.     minWidth = 10 + (tabFieldRight < quoteStringFieldRight ? quoteStringFieldRight : tabFieldRight);
  307.     if (minWidth < kMinWindowWidth) minWidth = kMinWindowWidth;
  308.     return minWidth;
  309. }
  310.  
  311.  
  312.  
  313. /*----------------------------------------------------------------------------
  314.     ChangeSubject 
  315.     
  316.     Change the subject in a message window.
  317.             
  318.     Entry:    wind = pointer to message window.
  319. ----------------------------------------------------------------------------*/
  320.  
  321. static void ChangeSubject (WindowPtr wind)
  322. {
  323.     TWindow **info;
  324.     TEHandle edit;
  325.     Str255 subject, title;
  326.     
  327.     info = (TWindow**)GetWRefCon(wind);
  328.     if ((**info).alias != nil) return;
  329.     edit = (**info).subjectField;
  330.     GetDialogItemText((**edit).hText, subject);
  331.     GetWTitle(wind, title);
  332.     if (*subject == 0) GetPString(kStrNoSubject, subject);
  333.     if (!EqualString(subject, title, true, true)) {
  334.         SetWTitle(wind, subject);
  335.     }
  336. }
  337.  
  338.  
  339.  
  340. /*----------------------------------------------------------------------------
  341.     ComputeFieldViewRect 
  342.     
  343.     Compute the view rectangle for a TextEdit field given the destination 
  344.     rectangle.
  345.             
  346.     Entry:    wind = pointer to message window.
  347.             *destRect = destination rectangle.
  348.             
  349.     Exit:    *viewRect = view rectangle.
  350.     
  351.     The TextEdit view rectangle is set to the destination rectangle
  352.     clipped to the window text rectangle. If the result is empty, the view
  353.     rectangle is set to an off-screen rectangle.
  354. ----------------------------------------------------------------------------*/
  355.  
  356. static void ComputeFieldViewRect (WindowPtr wind, Rect *destRect, Rect *viewRect)
  357. {
  358.     Rect textRect;
  359.     
  360.     GetTextRect(wind, &textRect);
  361.     *viewRect = *destRect;
  362.     if (viewRect->top < textRect.top) viewRect->top = textRect.top;
  363.     if (viewRect->bottom > textRect.bottom) viewRect->bottom = textRect.bottom;
  364.     if (viewRect->top >= viewRect->bottom) SetRect(viewRect, 0, 0x7700, 500, 0x77ff);
  365. }
  366.  
  367.  
  368.  
  369. /*----------------------------------------------------------------------------
  370.     AdjustLastFieldViewRect 
  371.     
  372.     Adjust the view rectangle for the last TextEdit field in a window.
  373.             
  374.     Entry:    wind = pointer to message window.
  375.     
  376.     If the view rectangle for the last TextEdit field does not extend to the
  377.     bottom of the window's text rectangle, it is extended to reach the bottom.
  378. ----------------------------------------------------------------------------*/
  379.  
  380. static void AdjustLastFieldViewRect (WindowPtr wind)
  381. {
  382.     TWindow **info;
  383.     TMsgFieldInfo **fields;
  384.     short numFields;
  385.     TEHandle lastEdit;
  386.     Rect textRect;
  387.     
  388.     info = (TWindow**)GetWRefCon(wind);
  389.     fields = (**info).fields;
  390.     numFields = (**info).numFields;
  391.     lastEdit = (*fields)[numFields - 1].edit;
  392.     GetTextRect(wind, &textRect);
  393.     if ((**lastEdit).viewRect.bottom < textRect.bottom) 
  394.         (**lastEdit).viewRect.bottom = textRect.bottom;
  395. }
  396.  
  397.  
  398.  
  399. /*----------------------------------------------------------------------------
  400.     AdjustScrollMax 
  401.     
  402.     Adjust the scroll bar maximum value.
  403.             
  404.     Entry:    wind = pointer to message window.
  405. ----------------------------------------------------------------------------*/
  406.  
  407. static void AdjustScrollMax (WindowPtr wind)
  408. {
  409.     TWindow **info;
  410.     TMsgFieldInfo **fields;
  411.     short numFields, max, windHeight, firstScrollingField; 
  412.     short top, bottom, height, lineHeight;
  413.     Rect textRect;
  414.     
  415.     info = (TWindow**)GetWRefCon(wind);
  416.     fields = (**info).fields;
  417.     numFields = (**info).numFields;
  418.     firstScrollingField = (**info).firstScrollingField;
  419.     top = (*fields)[firstScrollingField].top;
  420.     bottom = (*fields)[numFields-1].bottom;
  421.     GetTextRect(wind, &textRect);
  422.     windHeight = textRect.bottom - textRect.top;
  423.     if (bottom < textRect.bottom) bottom = textRect.bottom;
  424.     height = bottom - top;
  425.     max = height - windHeight;
  426.     if (max < 0) max = 0;
  427.     lineHeight = (**(**info).theTE).lineHeight;
  428.     SetControlMaximum((**info).vScroll, max/lineHeight);
  429. }
  430.  
  431.  
  432.  
  433. /*----------------------------------------------------------------------------
  434.     Scroll 
  435.     
  436.     Scroll a message window.
  437.             
  438.     Entry:    wind = pointer to message window.
  439.             dv = number of lines to scroll.
  440.             
  441.     If the vertical scroll bar's refCon is non-zero, the scroll bar max value is
  442.     adjusted after the scrolling operation. This is what you normally want.
  443.     The only exception is when scrolling in a TrackControl action procedure,
  444.     when you want to set the refCon to zero.
  445. ----------------------------------------------------------------------------*/
  446.  
  447. static void Scroll (WindowPtr wind, short dv)
  448. {
  449.     TWindow **info;
  450.     TMsgFieldInfo **fields, *f;
  451.     Rect r, destRect, viewRect, inval;
  452.     static RgnHandle rgn = nil;
  453.     TEHandle edit;
  454.     short numFields, i, firstScrollingField, lineHeight;
  455.  
  456.     info = (TWindow**)GetWRefCon(wind);
  457.     lineHeight = (**(**info).theTE).lineHeight;
  458.     dv = dv * lineHeight;
  459.     fields = (**info).fields;
  460.     GetViewRect(wind, &r);
  461.     InsetRect(&r, 0, kTextMargin);
  462.     if (rgn == nil) rgn = NewRgn();
  463.     ScrollRect(&r, 0, dv, rgn);
  464.     InvalRgn(rgn);
  465.     if (!((WindowPeek)wind)->hilited) {
  466.         inval = r;
  467.         if (dv > 0) {
  468.             if (inval.top + dv + lineHeight < inval.bottom) 
  469.                 inval.bottom = inval.top + dv + lineHeight;
  470.         } else {
  471.             if (inval.bottom + dv - lineHeight > inval.top) 
  472.                 inval.top = inval.bottom + dv - lineHeight;
  473.         }
  474.         InvalRect(&inval);
  475.         inval = r;
  476.         if (dv > 0) {
  477.             inval.top = inval.bottom - lineHeight;
  478.         } else {
  479.             inval.bottom = inval.top + lineHeight;
  480.         }
  481.         InvalRect(&inval);
  482.     }
  483.         
  484.     SetOrigin(0, 0);
  485.     numFields = (**info).numFields;
  486.     firstScrollingField = (**info).firstScrollingField;
  487.     for (i = firstScrollingField; i < numFields; i++) {
  488.         f = &(*fields)[i];
  489.         edit = f->edit;
  490.         f->top += dv;
  491.         f->bottom += dv;
  492.         destRect = (**edit).destRect;
  493.         OffsetRect(&destRect, 0, dv);
  494.         ComputeFieldViewRect(wind, &destRect, &viewRect);
  495.         (**edit).destRect = destRect;
  496.         (**edit).viewRect = viewRect;
  497.     }
  498.     AdjustLastFieldViewRect(wind);
  499.     if (GetControlReference((**info).vScroll) != 0) AdjustScrollMax(wind);
  500.     HandleUpdate(wind);
  501.     KillBalloon();
  502. }
  503.  
  504.  
  505.  
  506. /*----------------------------------------------------------------------------
  507.     ScrollRangeIntoView 
  508.     
  509.     Scroll a range of characters into view, if necessary.
  510.             
  511.     Entry:    wind = pointer to message window.
  512.             start = starting offset of text range in current field.
  513.             end = ending offet of text range in current field.
  514. ----------------------------------------------------------------------------*/
  515.  
  516. static void ScrollRangeIntoView (WindowPtr wind, short start, short end)
  517. {
  518.     TWindow **info;
  519.     TMsgFieldInfo **fields;
  520.     short curField, lineStart, lineEnd, top, lineHeight, vStart, vEnd;
  521.     short oldScrollVal, max, dv;
  522.     ControlHandle vScroll;
  523.     TEHandle edit;
  524.     Rect textRect;
  525.     Boolean tooBig;
  526.     short selStart, selEnd, savedClikStuff;
  527.  
  528.     AdjustScrollMax(wind);
  529.     info = (TWindow**)GetWRefCon(wind);
  530.     fields = (**info).fields;
  531.     curField = (**info).curField;
  532.     if (curField < (**info).firstScrollingField) return;
  533.     vScroll = (**info).vScroll;
  534.     edit = (*fields)[curField].edit;
  535.     
  536.     savedClikStuff = (**edit).clikStuff;
  537.     selStart = (**edit).selStart;
  538.     selEnd = (**edit).selEnd;
  539.     if (start == end && selStart < selEnd) {
  540.         if (start == selEnd) {
  541.             (**edit).clikStuff = 0;
  542.         } else if (end == selStart) {
  543.             (**edit).clikStuff = 0xffff;
  544.         }
  545.         lineStart = TEScrollGetTELineNumber(start, edit);
  546.         lineEnd = TEScrollGetTELineNumber(end, edit);
  547.     } else if (start < end) {
  548.         (**edit).clikStuff = 0xffff;
  549.         lineStart = TEScrollGetTELineNumber(start, edit);
  550.         (**edit).clikStuff = 0;
  551.         lineEnd = TEScrollGetTELineNumber(end, edit);
  552.     } else {
  553.         lineStart = TEScrollGetTELineNumber(start, edit);
  554.         lineEnd = TEScrollGetTELineNumber(end, edit);
  555.     }
  556.     (**edit).clikStuff = savedClikStuff;
  557.     
  558.     top = (**edit).destRect.top;
  559.     lineHeight = (**edit).lineHeight;
  560.     vStart = top + lineStart*lineHeight;
  561.     vEnd = top + (lineEnd+1)*lineHeight;
  562.     GetTextRect(wind, &textRect);
  563.     tooBig = (vEnd - vStart) > (textRect.bottom - textRect.top);
  564.     if (vEnd > textRect.bottom) {
  565.         if (vStart < textRect.bottom - lineHeight) return;
  566.         if (tooBig) {
  567.             dv = textRect.top - vStart;
  568.         } else {
  569.             dv = textRect.bottom - vEnd;
  570.         }
  571.     } else if (vStart < textRect.top) {
  572.         if (vEnd > textRect.top + lineHeight) return;
  573.         if (tooBig) {
  574.             dv = textRect.bottom - vEnd;
  575.         } else {
  576.             dv = textRect.top - vStart;
  577.         }
  578.     } else {
  579.         AdjustScrollMax(wind);
  580.         return;
  581.     }
  582.     dv = dv/lineHeight;
  583.     oldScrollVal = GetControlValue(vScroll);
  584.     max = GetControlMaximum(vScroll);
  585.     if (oldScrollVal - dv > max) dv = oldScrollVal - max;
  586.     Scroll(wind, dv);
  587.     SetControlValue(vScroll, oldScrollVal - dv);
  588. }
  589.  
  590.  
  591.  
  592. /*----------------------------------------------------------------------------
  593.     ScrollSelectionIntoView 
  594.     
  595.     Scroll the current selection into view, if necessary.
  596.             
  597.     Entry:    wind = pointer to message window.
  598. ----------------------------------------------------------------------------*/
  599.  
  600. static void ScrollSelectionIntoView (WindowPtr wind)
  601. {
  602.     TWindow **info;
  603.     TMsgFieldInfo **fields;
  604.     short curField;
  605.     TEHandle edit;
  606.  
  607.     info = (TWindow**)GetWRefCon(wind);
  608.     fields = (**info).fields;
  609.     curField = (**info).curField;
  610.     if (curField < (**info).firstScrollingField) return;
  611.     edit = (*fields)[curField].edit;
  612.     ScrollRangeIntoView (wind, (**edit).selStart, (**edit).selEnd);
  613. }
  614.  
  615.  
  616.  
  617. /*----------------------------------------------------------------------------
  618.     ScrollToMiddle 
  619.     
  620.     Scroll a given point in a field to the middle of the window, if necessary.
  621.             
  622.     Entry:    wind = pointer to message window.
  623.             offset = offset of character in current field to scroll to middle.
  624. ----------------------------------------------------------------------------*/
  625.  
  626. static void ScrollToMiddle (WindowPtr wind, short offset)
  627. {
  628.     TWindow **info;
  629.     TMsgFieldInfo **fields;
  630.     short curField;
  631.     ControlHandle vScroll;
  632.     TEHandle edit;
  633.     short lineStart, top, lineHeight, vStart;
  634.     short oldScrollVal, max, dv, textHeight;
  635.     Rect textRect;
  636.     short savedClikStuff;
  637.  
  638.     AdjustScrollMax(wind);
  639.     info = (TWindow**)GetWRefCon(wind);
  640.     curField = (**info).curField;
  641.     if (curField < (**info).firstScrollingField) return;
  642.     fields = (**info).fields;
  643.     vScroll = (**info).vScroll;
  644.     edit = (*fields)[curField].edit;
  645.  
  646.     savedClikStuff = (**edit).clikStuff;
  647.     (**edit).clikStuff = 0xffff;
  648.     lineStart = TEScrollGetTELineNumber(offset, edit);
  649.     (**edit).clikStuff = savedClikStuff;
  650.     top = (**edit).destRect.top;
  651.     lineHeight = (**edit).lineHeight;
  652.     vStart = top + lineStart*lineHeight;
  653.     GetTextRect(wind, &textRect);
  654.     if (vStart >= textRect.top && vStart <= textRect.bottom - lineHeight) return;
  655.     textHeight = (textRect.bottom - textRect.top) / lineHeight;
  656.     dv = (textRect.top - vStart)/lineHeight + (textHeight >> 1);
  657.     oldScrollVal = GetControlValue(vScroll);
  658.     max = GetControlMaximum(vScroll);
  659.     if (oldScrollVal - dv > max) dv = oldScrollVal - max;
  660.     Scroll(wind, dv);
  661.     SetControlValue(vScroll, oldScrollVal - dv); 
  662. }
  663.  
  664.  
  665.  
  666. /*----------------------------------------------------------------------------
  667.     GetPageHeight 
  668.     
  669.     Get the page height.
  670.             
  671.     Entry:    wind = pointer to message window.
  672.     
  673.     Exit:    function result = page height.
  674. ----------------------------------------------------------------------------*/
  675.  
  676. static short GetPageHeight (WindowPtr wind)
  677. {
  678.     TWindow **info;
  679.     TEHandle theTE;
  680.     short lineHeight;
  681.     Rect r;
  682.  
  683.     info = (TWindow**)GetWRefCon(wind);
  684.     theTE = (**info).theTE;
  685.     lineHeight = (**theTE).lineHeight;
  686.     GetTextRect(wind, &r);
  687.     return (r.bottom - r.top) / lineHeight - 1;
  688. }
  689.  
  690.  
  691.  
  692. /*----------------------------------------------------------------------------
  693.     ScrollAction 
  694.     
  695.     Scroll bar action procedure.
  696.             
  697.     Entry:    vScroll = handle to vertical scroll bar control
  698.             part = part code.
  699. ----------------------------------------------------------------------------*/
  700.  
  701. static pascal void ScrollAction (ControlHandle vScroll, short part)
  702. {
  703.     WindowPtr wind;
  704.     short val, max, page, dv;
  705.  
  706.     wind = (**vScroll).contrlOwner;
  707.     val = (**vScroll).contrlValue;
  708.     max = (**vScroll).contrlMax;
  709.     page = GetPageHeight(wind);
  710.     dv = 0;
  711.     switch (part) {
  712.         case inUpButton:
  713.             dv = val > 0 ? 1 : 0;
  714.             break;
  715.         case inDownButton:
  716.             dv = val < max ? -1 : 0;
  717.             break;
  718.         case inPageUp:
  719.             dv = val > page ? page : val;
  720.             break;
  721.         case inPageDown:
  722.             dv = val < max - page ? -page : val - max;
  723.             break;
  724.         case kScrollToHome:
  725.             dv = val;
  726.             break;
  727.         case kScrollToEnd:
  728.             dv = val - max;
  729.             break;
  730.     }
  731.     if (dv != 0) {
  732.         SetControlValue(vScroll, val - dv);
  733.         Scroll(wind, dv);
  734.     }
  735. }
  736.  
  737.  
  738.  
  739. /*----------------------------------------------------------------------------
  740.     AutoScroll 
  741.     
  742.     Handle message window autoscrolling.
  743.             
  744.     Exit:    function result = true
  745. ----------------------------------------------------------------------------*/
  746.  
  747. static pascal Boolean AutoScroll (void)
  748. {
  749.     WindowPtr wind;
  750.     TWindow **info;
  751.     ControlHandle vScroll;
  752.     TMsgFieldInfo **fields;
  753.     short curField;
  754.     TEHandle edit;
  755.     Rect r;
  756.     Point where;
  757.     short val, max;
  758.  
  759.     wind = MyFrontWindow();
  760.     if (wind == nil) return true;
  761.     info = (TWindow**)GetWRefCon(wind);
  762.     vScroll = (**info).vScroll;
  763.     fields = (**info).fields;
  764.     curField = (**info).curField;
  765.     edit = (*fields)[curField].edit;
  766.     val = GetControlValue(vScroll);
  767.     max = GetControlMaximum(vScroll);
  768.     GetTextRect(wind, &r);
  769.     GetMouse(&where);
  770.     ClipRect(&wind->portRect);
  771.     if (where.v < r.top && val > 0) {
  772.         Scroll(wind, 1);
  773.         SetControlValue(vScroll, val-1);
  774.     } else if (where.v > r.bottom && val < max) {
  775.         Scroll(wind, -1);
  776.         SetControlValue(vScroll, val+1);
  777.     }
  778.     r = (**edit).viewRect;
  779.     ClipRect(&r);
  780.     return true;
  781. }
  782.  
  783.  
  784.  
  785. /*----------------------------------------------------------------------------
  786.     AddFieldInfo 
  787.     
  788.     Add one new field to array of field info.
  789.             
  790.     Entry:    wind = pointer to message window.
  791.             edit = handle to TextEdit record.
  792.             sepLine = true if field preceeded by gray separator line.
  793.             labelKind = kind of label for this field.
  794.             labelIndex = index in STR# 128 of field label, or 0 if none.
  795. ----------------------------------------------------------------------------*/
  796.  
  797. static void AddFieldInfo (WindowPtr wind, TEHandle edit, Boolean sepLine, 
  798.     TMsgFieldLabelKind labelKind, short labelIndex)
  799. {
  800.     TWindow **info;
  801.     short index;
  802.     TMsgFieldInfo **fields;
  803.     TMsgFieldInfo f;
  804.     short nLines;
  805.     short lineHeight, height, h;
  806.     static short v;
  807.     Rect textRect, destRect, viewRect;
  808.     Str255 label;
  809.         
  810.     if (labelIndex > 0) GetPString(labelIndex, label);
  811.     info = (TWindow**)GetWRefCon(wind);
  812.     index = (**info).numFields;
  813.     fields = (**info).fields;
  814.     f.edit = edit;
  815.     f.sepLine = sepLine;
  816.     f.labelKind = labelKind;
  817.     if (labelIndex > 0) BlockMoveData(label, f.label, *label+1);
  818.         
  819.     if (index == (**info).firstScrollingField) v = (**info).panelHeight + kTextMargin;
  820.     f.top = v;
  821.     lineHeight = (**edit).lineHeight;
  822.     if (sepLine) v += lineHeight;
  823.     switch (labelKind) {
  824.         case kNoMsgFieldLabel:
  825.             h = kTextMargin;
  826.             break;
  827.         case kMsgFieldLabelLeft:
  828.             h = (**info).labelRight;
  829.             break;
  830.         case kMsgFieldLabelTop:
  831.             h = kTextMargin;
  832.             v += lineHeight;
  833.             break;
  834.     }
  835.     GetTextRect(wind, &textRect);
  836.     SetRect(&destRect, h, v, textRect.right, v + 1000);
  837.     (**edit).viewRect = (**edit).destRect = destRect;
  838.     TECalText(edit);
  839.     f.nLines = nLines = TEScrollNumTELines(edit);
  840.     height = nLines * lineHeight;
  841.     v += height;
  842.     destRect.bottom = f.bottom = v;
  843.     ComputeFieldViewRect(wind, &destRect, &viewRect);
  844.     (**edit).viewRect = viewRect;
  845.     (**edit).destRect = destRect;
  846.     (*fields)[index] = f;
  847.     (**info).numFields++;
  848.     KillBalloon();
  849. }
  850.  
  851.  
  852.  
  853. /*----------------------------------------------------------------------------
  854.     RebuildFields 
  855.     
  856.     Rebuild the field info for a message window.
  857.             
  858.     Entry:    wind = pointer to message window.
  859. ----------------------------------------------------------------------------*/
  860.  
  861. static void RebuildFields (WindowPtr wind)
  862. {
  863.     TWindow **info;
  864.     TMsgFieldInfo **fields;
  865.     
  866.     info = (TWindow**)GetWRefCon(wind);
  867.     fields = (**info).fields;
  868.     
  869.     if ((**info).showDetails) {
  870.         (*fields)[0].edit = (**info).tabField;
  871.         (*fields)[1].edit = (**info).quoteStringField;
  872.         (**info).firstScrollingField = (**info).numFields = 2;
  873.     } else {
  874.         (**info).firstScrollingField = (**info).numFields = 0;
  875.     }
  876.     if ((**info).newsIcon) { 
  877.         AddFieldInfo(wind, (**info).newsgroupsField, false, kMsgFieldLabelLeft, 
  878.             kStrNewsgroups);
  879.     }
  880.     if ((**info).mailIcon) {
  881.         AddFieldInfo(wind, (**info).toField, false, kMsgFieldLabelLeft,
  882.             kStrTo);
  883.     }
  884.     AddFieldInfo(wind, (**info).subjectField, false, kMsgFieldLabelLeft, 
  885.         kStrSubject);
  886.     if ((**info).showDetails) {
  887.         if ((**info).mailIcon) {
  888.             AddFieldInfo(wind, (**info).ccField, false, kMsgFieldLabelLeft, 
  889.                 kStrCc);
  890.             AddFieldInfo(wind, (**info).bccField, false, kMsgFieldLabelLeft,
  891.                 kStrBcc);
  892.         }
  893.         AddFieldInfo(wind, (**info).replytoField, false, kMsgFieldLabelLeft, 
  894.             kStrReplyto);
  895.         if ((**info).newsIcon) {
  896.             AddFieldInfo(wind, (**info).followuptoField, false, kMsgFieldLabelLeft,
  897.                 kStrFollowupto);
  898.         }
  899.         AddFieldInfo(wind, (**info).keywordsField, false, kMsgFieldLabelLeft, 
  900.             kStrKeywords);
  901.         if ((**info).newsIcon) {
  902.             AddFieldInfo(wind, (**info).distributionField, false, kMsgFieldLabelLeft,
  903.                 kStrDistribution);
  904.             AddFieldInfo(wind, (**info).extraNewsField, true, kMsgFieldLabelTop,
  905.                 kStrExtraNewsHeaderLines);
  906.         }
  907.         if ((**info).mailIcon) {
  908.             AddFieldInfo(wind, (**info).extraMailField, true, kMsgFieldLabelTop,
  909.                 kStrExtraMailHeaderLines);
  910.         }
  911.         AddFieldInfo(wind, (**info).theTE, true, kMsgFieldLabelTop, 
  912.             kStrBody);
  913.         AddFieldInfo(wind, (**info).signatureField, true, kMsgFieldLabelTop, 
  914.             kStrSignature);
  915.     } else {
  916.         AddFieldInfo(wind, (**info).theTE, true, kNoMsgFieldLabel, 0);
  917.     }
  918.     
  919.     AdjustLastFieldViewRect(wind);
  920.     AdjustScrollMax(wind);
  921.     SetControlValue((**info).vScroll, 0);
  922. }
  923.  
  924.  
  925.  
  926. /*----------------------------------------------------------------------------
  927.     ChangeDisplayedFields 
  928.     
  929.     Change the currently displayed fields in a message window.
  930.             
  931.     Entry:    wind = pointer to message window.
  932. ----------------------------------------------------------------------------*/
  933.  
  934. static void ChangeDisplayedFields (WindowPtr wind)
  935. {
  936.     TWindow **info;
  937.     TMsgFieldInfo **fields, *f;
  938.     TEHandle oldEdit, newEdit;
  939.     short oldField, newField, numFields;
  940.     Rect viewRect;
  941.     
  942.     info = (TWindow**)GetWRefCon(wind);
  943.     fields = (**info).fields;
  944.     oldField = (**info).curField;
  945.     oldEdit = (*fields)[oldField].edit;
  946.     RebuildFields(wind);
  947.     numFields = (**info).numFields;
  948.     
  949.     for (newField = 0, f = *fields; newField < numFields; newField++, f++) {
  950.         newEdit = f->edit;
  951.         if (oldEdit == newEdit) break;
  952.         if (newEdit == (**info).newsgroupsField ||
  953.             newEdit == (**info).toField ||
  954.             newEdit == (**info).subjectField)
  955.         {
  956.             if ((**newEdit).teLength == 0) break;
  957.         }
  958.     }
  959.     if (newField >= numFields) {
  960.         InitCurField(wind);
  961.         newField = (**info).curField;
  962.         newEdit = (*fields)[newField].edit;
  963.     }
  964.     (**info).curField = newField;
  965.     if (newEdit != oldEdit) {
  966.         if (gHaveTEOutlineHilite) TEFeatureFlag(teFOutlineHilite, TEBitClear, oldEdit);
  967.         TESetSelect(0, 0, oldEdit);
  968.         TEDeactivate(oldEdit);
  969.         MyTEActivate(newEdit);
  970.         if (gHaveTEOutlineHilite) TEFeatureFlag(teFOutlineHilite, TEBitSet, newEdit);
  971.     }
  972.     ScrollSelectionIntoView(wind);
  973.     GetViewRect(wind, &viewRect);
  974.     InvalRect(&viewRect);
  975.     KillBalloon();
  976. }
  977.  
  978.  
  979.  
  980. /*----------------------------------------------------------------------------
  981.     UncheckNewsIcon 
  982.     
  983.     Uncheck the news icon in a message window.
  984.             
  985.     Entry:    wind = pointer to message window.
  986. ----------------------------------------------------------------------------*/
  987.  
  988. void UncheckNewsIcon (WindowPtr wind)
  989. {
  990.     TWindow **info;
  991.     Rect r;
  992.  
  993.     info = (TWindow**)GetWRefCon(wind);
  994.     (**info).newsIcon = false;
  995.     SetRect(&r, kNewsIconH - kCheckMarkDeltaH, kIconV, kNewsIconH - 1, kIconV + 32);
  996.     InvalRect(&r);
  997.     ChangeDisplayedFields(wind);
  998.     HandleUpdate(wind);
  999. }
  1000.  
  1001.  
  1002.  
  1003. /*----------------------------------------------------------------------------
  1004.     ResizeContents 
  1005.     
  1006.     Adjust a message window's contents after a window size change (grow
  1007.     or zoom).
  1008.             
  1009.     Entry:    wind = pointer to message window.
  1010. ----------------------------------------------------------------------------*/
  1011.  
  1012. static void ResizeContents (WindowPtr wind)
  1013. {
  1014.     TWindow **info;
  1015.     short width, height, panelHeight, sendButtonTop;
  1016.     ControlHandle vScroll, sendButton;
  1017.     Rect r;
  1018.  
  1019.     info = (TWindow**)GetWRefCon(wind);
  1020.     panelHeight = (**info).panelHeight;
  1021.     vScroll = (**info).vScroll;
  1022.     sendButton = (**info).sendButton;
  1023.     width = wind->portRect.right;
  1024.     height = wind->portRect.bottom;
  1025.     
  1026.     SetRect(&r, width-15, panelHeight-1, width+1, height-14);
  1027.     (**vScroll).contrlRect = r;
  1028.  
  1029.     sendButtonTop = (**info).showLabels ? kSendButtonTopLabels : kSendButtonTopNoLabels;
  1030.     SetRect(&r, width-70, sendButtonTop, width-10, sendButtonTop+20);
  1031.     (**sendButton).contrlRect = r;    
  1032.     
  1033.     RebuildFields(wind);
  1034.     InvalRect(&wind->portRect);
  1035.     ScrollSelectionIntoView(wind);
  1036. }
  1037.  
  1038.  
  1039.  
  1040. /*----------------------------------------------------------------------------
  1041.     ChangeCurField 
  1042.     
  1043.     Change the currently active field in a message window.
  1044.             
  1045.     Entry:    wind = pointer to message window.
  1046.             newCurField = index in field info array of new active field.
  1047. ----------------------------------------------------------------------------*/
  1048.  
  1049. static void ChangeCurField (WindowPtr wind, short newCurField)
  1050. {
  1051.     TWindow **info;
  1052.     TMsgFieldInfo **fields;
  1053.     short curField;
  1054.     TEHandle edit;
  1055.     Str255 tabStopsStr;
  1056.     long tabStops;
  1057.     
  1058.     info = (TWindow**)GetWRefCon(wind);
  1059.     fields = (**info).fields;
  1060.     curField = (**info).curField;
  1061.     edit = (*fields)[curField].edit;
  1062.     if (gHaveTEOutlineHilite) TEFeatureFlag(teFOutlineHilite, TEBitClear, edit);
  1063.     TESetSelect(0, 0, edit);
  1064.     TEDeactivate(edit);
  1065.     if (edit == (**info).subjectField) ChangeSubject(wind);
  1066.     if (edit == (**info).tabField) {
  1067.         GetDialogItemText((**edit).hText, tabStopsStr);
  1068.         StringToNum(tabStopsStr, &tabStops);
  1069.         (**info).tabStops = tabStops;
  1070.     }
  1071.     edit = (*fields)[newCurField].edit;
  1072.     MyTEActivate(edit);
  1073.     if (gHaveTEOutlineHilite) TEFeatureFlag(teFOutlineHilite, TEBitSet, edit);
  1074.     (**info).curField = newCurField;
  1075. }
  1076.  
  1077.  
  1078.  
  1079. /*----------------------------------------------------------------------------
  1080.     GetFieldIndex 
  1081.     
  1082.     Get the index of a field in the fields array.
  1083.             
  1084.     Entry:    wind = pointer to message window.
  1085.             edit = handle to TextEdit field.
  1086.             
  1087.     Exit:    function result = index of field in fields array, or -1
  1088.             if not found.
  1089. ----------------------------------------------------------------------------*/
  1090.  
  1091. static short GetFieldIndex (WindowPtr wind, TEHandle edit)
  1092. {
  1093.     TWindow **info;
  1094.     TMsgFieldInfo **fields;
  1095.     short numFields, i;
  1096.     
  1097.     info = (TWindow**)GetWRefCon(wind);
  1098.     fields = (**info).fields;
  1099.     numFields = (**info).numFields;
  1100.     
  1101.     for (i = 0; i < numFields; i++)
  1102.         if ((*fields)[i].edit == edit) break;
  1103.     return i < numFields ? i : -1;
  1104. }
  1105.  
  1106.  
  1107.  
  1108. /*----------------------------------------------------------------------------
  1109.     AdjustFieldHeight 
  1110.     
  1111.     Adjust the height of a field after a field change.
  1112.             
  1113.     Entry:    wind = pointer to message window.
  1114.             edit = handle to TextEdit field to adjust.
  1115. ----------------------------------------------------------------------------*/
  1116.  
  1117. static void AdjustFieldHeight (WindowPtr wind, TEHandle edit)
  1118. {
  1119.     TWindow **info;
  1120.     TMsgFieldInfo **fields, *f, *g;
  1121.     short fieldIndex, i, nLines, lineHeight, numFields, v, height;
  1122.     Rect destRect, viewRect, r;
  1123.     short oldBottom, newBottom;
  1124.     char state;
  1125.     Boolean active;
  1126.     
  1127.     info = (TWindow**)GetWRefCon(wind);
  1128.     fields = (**info).fields;
  1129.     numFields = (**info).numFields;
  1130.     
  1131.     fieldIndex = GetFieldIndex(wind, edit);
  1132.     if (fieldIndex < (**info).firstScrollingField) return;
  1133.     active = ((WindowPeek)wind)->hilited;
  1134.     
  1135.     state = MyHGetState(fields);
  1136.     MyHLock(fields);
  1137.     f = &(*fields)[fieldIndex];
  1138.     nLines = TEScrollNumTELines(edit);
  1139.     oldBottom = f->bottom;
  1140.     if (nLines != f->nLines) {
  1141.         lineHeight = (**edit).lineHeight;
  1142.         f->nLines = nLines;
  1143.         v = f->top;
  1144.         for (i = fieldIndex, g = f; i < numFields; i++, g++) {
  1145.             edit = g->edit;
  1146.             g->top = v;
  1147.             if (g->sepLine) v += lineHeight;
  1148.             if (g->labelKind == kMsgFieldLabelTop) v += lineHeight;
  1149.             height = g->nLines * lineHeight;
  1150.             destRect = (**edit).destRect;
  1151.             destRect.top = v;
  1152.             destRect.bottom = v + height;
  1153.             ComputeFieldViewRect(wind, &destRect, &viewRect);
  1154.             v += height;
  1155.             g->bottom = v;
  1156.             (**edit).viewRect = viewRect;
  1157.             (**edit).destRect = destRect;
  1158.         }
  1159.         AdjustLastFieldViewRect(wind);
  1160.         newBottom = f->bottom;
  1161.         if (active) {
  1162.             GetViewRect(wind, &r);
  1163.             r.top = oldBottom < newBottom ? oldBottom : newBottom;
  1164.             InvalRect(&r);
  1165.             HandleUpdate(wind);
  1166.         }
  1167.         AdjustScrollMax(wind);
  1168.         KillBalloon();
  1169.     }
  1170.     MyHSetState(fields, state);
  1171.     ScrollSelectionIntoView(wind);
  1172.     if (!active) {
  1173.         GetViewRect(wind, &viewRect);
  1174.         InvalRect(&viewRect);
  1175.         HandleUpdate(wind);
  1176.     }
  1177. }
  1178.  
  1179.  
  1180.  
  1181. /*----------------------------------------------------------------------------
  1182.     AdjustCurFieldHeight 
  1183.     
  1184.     Adjust the height of the current field after a field change.
  1185.             
  1186.     Entry:    wind = pointer to message window.
  1187. ----------------------------------------------------------------------------*/
  1188.  
  1189. static void AdjustCurFieldHeight (WindowPtr wind)
  1190. {
  1191.     TWindow **info;
  1192.     TMsgFieldInfo **fields;
  1193.     TEHandle edit;
  1194.     short curField;
  1195.     
  1196.     info = (TWindow**)GetWRefCon(wind);
  1197.     fields = (**info).fields;
  1198.     curField = (**info).curField;
  1199.     if (curField < (**info).firstScrollingField) return;
  1200.     edit = (*fields)[curField].edit;
  1201.     AdjustFieldHeight(wind, edit);
  1202. }
  1203.  
  1204.  
  1205.  
  1206. /*----------------------------------------------------------------------------
  1207.     InitializeGroupList 
  1208.     
  1209.     Initialize the group list for a new message window.
  1210.             
  1211.     Entry:    wind = pointer to active window, or nil if none.
  1212.             newsgroups = handle to "Newsgroups" textedit field.
  1213.             
  1214.     Exit:    function result = error code.
  1215. ----------------------------------------------------------------------------*/
  1216.  
  1217. static OSErr InitializeGroupList (WindowPtr wind, TEHandle newsgroups)
  1218. {
  1219.     TWindow **info, **parentInfo;
  1220.     ListHandle theList;
  1221.     TGroup **groupArray;
  1222.     Point theCell;
  1223.     short numSelected, cellDataLen, index;
  1224.     CStr255 groupsStr;
  1225.     TWindowKind kind;
  1226.     OSErr err = noErr;
  1227.  
  1228.     kind = GetMyWindowKind(wind);
  1229.     if (kind == kNotOurWind) return noErr;
  1230.     info = (TWindow**)GetWRefCon(wind);
  1231.     switch (kind) {
  1232.         case kGroup:
  1233.             theList = (**info).theList;
  1234.             groupArray = (**info).groupArray;
  1235.             SetPt(&theCell, 0, 0);
  1236.             numSelected = 0;
  1237.             while (LGetSelect(true, &theCell, theList)) {
  1238.                 numSelected++;
  1239.                 if (numSelected > 50) {
  1240.                     ErrorMessageNumber(kStrTooManyGroups);
  1241.                     return userCanceledErr;
  1242.                 }
  1243.                 cellDataLen = 2;
  1244.                 LGetCell(&index, &cellDataLen, theCell, theList);
  1245.                 strcpy(groupsStr, *gGroupNames + (*groupArray)[index].nameOffset);
  1246.                 theCell.v++;
  1247.                 if (numSelected > 1) TEInsert(", ", 2, newsgroups);
  1248.                 TEInsert(groupsStr, strlen(groupsStr), newsgroups);
  1249.             }
  1250.             if (numSelected > 5) {
  1251.                 err = TooManyGroupsDialog(numSelected);
  1252.                 if (err != noErr) return err;
  1253.             }
  1254.             break;
  1255.         case kSubject:
  1256.             strcpy(groupsStr, *gGroupNames + (**info).groupNameOffset);
  1257.             err = MyTESetText(groupsStr, strlen(groupsStr), newsgroups);
  1258.             if (err != noErr) return err;
  1259.             break;
  1260.         case kArticle:
  1261.             if ((**info).parentWindow != nil) {
  1262.                 parentInfo = (TWindow**)GetWRefCon((**info).parentWindow);
  1263.                 strcpy(groupsStr, *gGroupNames + (**parentInfo).groupNameOffset);
  1264.             }
  1265.             err = MyTESetText(groupsStr, strlen(groupsStr), newsgroups);
  1266.             if (err != noErr) return err;
  1267.             break;
  1268.     }
  1269.     return noErr;
  1270. }
  1271.  
  1272.  
  1273.  
  1274. /*----------------------------------------------------------------------------
  1275.     DrawIcon 
  1276.     
  1277.     Draw a single icon and checkmark in a message window panel area.
  1278.             
  1279.     Entry:    h = horizontal coord of icon.
  1280.             id = resource id of icon family.
  1281.             checked = true to draw check mark.
  1282.             showLabel = true to draw label under icon.
  1283.             labelIndex = index in STR# 128 resource of label.
  1284. ----------------------------------------------------------------------------*/
  1285.  
  1286. static void DrawIcon (short h, short id, Boolean checked, Boolean showLabel,
  1287.      short labelIndex)
  1288. {
  1289.     Rect iconRect;
  1290.     TextStyle savedStyle;
  1291.     Str255 label;
  1292.     short labelWidth;
  1293.     
  1294.     SetRect(&iconRect, h, kIconV, h+32, kIconV+32);
  1295.     PlotIconID(&iconRect, 0, ttNone, id);
  1296.     GetPortTextStyle(&savedStyle);
  1297.     if (checked) {
  1298.         TextFont(systemFont);
  1299.         TextSize(12);
  1300.         TextFace(bold);
  1301.         MoveTo(h - kCheckMarkDeltaH, kCheckMarkV);
  1302.         DrawChar(checkMark);
  1303.     }
  1304.     if (showLabel) {
  1305.         GetPString(labelIndex, label);
  1306.         TextFont(applFont);
  1307.         TextSize(9);
  1308.         TextFace(0);
  1309.         labelWidth = StringWidth(label);
  1310.         MoveTo(h + 13 - (labelWidth >> 1), kIconV + 40);
  1311.         DrawString(label);
  1312.     }
  1313.     SetPortTextStyle(&savedStyle);
  1314. }
  1315.  
  1316.  
  1317.  
  1318. /*----------------------------------------------------------------------------
  1319.     QuoteText 
  1320.     
  1321.     Quote article text.
  1322.             
  1323.     Entry:    text = handle to text.
  1324.             start = beginning position in text to quote.
  1325.             end = ending position in text to quote.
  1326.             
  1327.     Exit:    function result = error code.
  1328.             text = handle to quoted text.
  1329.             
  1330.     The quote string is inserted at the beginning of each line in the
  1331.     range [start, end).
  1332. ----------------------------------------------------------------------------*/
  1333.  
  1334. static OSErr QuoteText (Handle text, long start, long end)
  1335. {
  1336.     long oldLen, len, deltaLen, quoteStringLen;
  1337.     char *p, *pEnd, *q;
  1338.     OSErr err = noErr;
  1339.  
  1340.     quoteStringLen = strlen(gPrefs.quoteString);
  1341.     oldLen = MyGetHandleSize(text);
  1342.     deltaLen = 0;
  1343.     for (p = *text + start, pEnd = *text + end; p < pEnd; p++)
  1344.         if (*p == CR) deltaLen += quoteStringLen;
  1345.     if (start < end && *(*text + end - 1) != CR) deltaLen += quoteStringLen;
  1346.     len = oldLen + deltaLen;
  1347.     
  1348.     err = MySetHandleSize(text, len);    
  1349.     if (err != noErr) return err;
  1350.     
  1351.     BlockMoveData(*text + start, *text + start + deltaLen, oldLen - start);
  1352.     
  1353.     p = *text + start + deltaLen;
  1354.     pEnd = *text + end + deltaLen;
  1355.     q = *text + start;
  1356.     while (p < pEnd) {
  1357.         BlockMoveData(gPrefs.quoteString, q, quoteStringLen);
  1358.         q += quoteStringLen;
  1359.         while (p < pEnd && *p != CR) *q++ = *p++;
  1360.         p++;
  1361.         if (p < pEnd) *q++ = CR;
  1362.     }
  1363.     
  1364.     return noErr;
  1365. }
  1366.  
  1367.  
  1368.  
  1369. /*----------------------------------------------------------------------------
  1370.     MoveHeaderLine 
  1371.     
  1372.     Move a header line from a text block to a TextEdit field.
  1373.             
  1374.     Entry:    text = handle to text block.
  1375.             key = header keyword.
  1376.             field = handle to TextEdit field.
  1377.             
  1378.     Exit:    function result = error code.
  1379. ----------------------------------------------------------------------------*/
  1380.  
  1381.  
  1382. static OSErr MoveHeaderLine (Handle text, char *key, TEHandle field)
  1383. {
  1384.     Handle header;
  1385.     OSErr err = noErr;
  1386.     
  1387.     err = FindHeaderHandle(text, key, &header);
  1388.     if (err != noErr) return err;
  1389.     if (header == nil) return noErr;
  1390.     MyDisposeHandle((**field).hText);
  1391.     (**field).hText = header;
  1392.     DeleteHeaderLine(text, key);
  1393.     return noErr;
  1394. }
  1395.  
  1396.  
  1397.  
  1398. /*----------------------------------------------------------------------------
  1399.     InitFieldInfo 
  1400.     
  1401.     Initialize field info for a new message window.
  1402.             
  1403.     Entry:    wind = pointer to message window.
  1404. ----------------------------------------------------------------------------*/
  1405.  
  1406. static void InitFieldInfo (WindowPtr wind)
  1407. {
  1408.     TWindow **info;
  1409.     short maxWidth, width;
  1410.     Str255 str;
  1411.     
  1412.     info = (TWindow**)GetWRefCon(wind);
  1413.     
  1414.     GetPString(kStrNewsgroups, str);
  1415.     maxWidth = StringWidth(str);
  1416.     GetPString(kStrTo, str);
  1417.     width = StringWidth(str);
  1418.     if (width > maxWidth) maxWidth = width;
  1419.     GetPString(kStrSubject, str);
  1420.     width = StringWidth(str);
  1421.     if (width > maxWidth) maxWidth = width;
  1422.     GetPString(kStrCc, str);
  1423.     width = StringWidth(str);
  1424.     if (width > maxWidth) maxWidth = width;
  1425.     GetPString(kStrBcc, str);
  1426.     width = StringWidth(str);
  1427.     if (width > maxWidth) maxWidth = width;
  1428.     GetPString(kStrReplyto, str);
  1429.     width = StringWidth(str);
  1430.     if (width > maxWidth) maxWidth = width;
  1431.     GetPString(kStrFollowupto, str);
  1432.     width = StringWidth(str);
  1433.     if (width > maxWidth) maxWidth = width;
  1434.     GetPString(kStrKeywords, str);
  1435.     width = StringWidth(str);
  1436.     if (width > maxWidth) maxWidth = width;
  1437.     GetPString(kStrDistribution, str);
  1438.     width = StringWidth(str);
  1439.     if (width > maxWidth) maxWidth = width;
  1440.     
  1441.     (**info).labelRight = maxWidth + kTextMargin + 4;
  1442.  
  1443.     RebuildFields(wind);
  1444.     InitCurField(wind);
  1445. }
  1446.  
  1447.  
  1448.  
  1449. /*----------------------------------------------------------------------------
  1450.     MyTENew 
  1451.     
  1452.     Create a TexTedit record.
  1453.             
  1454.     Exit:    function result = handle to new TE record.
  1455. ----------------------------------------------------------------------------*/
  1456.  
  1457. static TEHandle MyTENew (void)
  1458. {
  1459.     Rect r;
  1460.     TEHandle edit;
  1461.     
  1462.     SetRect(&r, 0, 0x7700, 500, 0x7fff);
  1463.     edit = TENew(&r, &r);
  1464.     (**edit).clickLoop = gAutoScrollUPP;
  1465.     return edit;
  1466. }
  1467.  
  1468.  
  1469.  
  1470. /*----------------------------------------------------------------------------
  1471.     InsertText 
  1472.     
  1473.     Insert new text (pasted or dragged) into a field.
  1474.             
  1475.     Entry:    wind = pointer to message window
  1476.             text = handle to text to insert.
  1477.             len = length of text to insert.
  1478.             edit = handle to TextEdit field for inserted text.
  1479.             offset = offset into TextEdit field for inserted text.
  1480.             deleteCurSel = true to delete current selected text.
  1481.             quote = true to insert text quoted.
  1482.             paste = true if paste, false if drag.
  1483.             
  1484.     Exit:    function result = error code.
  1485. ----------------------------------------------------------------------------*/
  1486.  
  1487. static OSErr InsertText (WindowPtr wind, Handle text, long len, TEHandle edit, 
  1488.     short offset, Boolean deleteCurSel, Boolean quote, Boolean paste)
  1489. {
  1490.     TWindow **info;
  1491.     short curField;
  1492.     TMsgFieldInfo **fields;
  1493.     TEHandle curEdit, quoteStringField;
  1494.     Handle newText = nil;
  1495.     char quoteString[11];
  1496.     long quoteStringLen, maxLen, quotedTextLen, newLen;
  1497.     OSErr err = noErr;
  1498.     unsigned char *p, *pEnd, *q;
  1499.     short n, tabStops, deletedTextLen, editIndex;
  1500.     Boolean isTabField, isQuoteStringField, isBodyField, isBodyOrSigField;
  1501.     Boolean extraSpaceDeleted, extraSpaceAddedInFront;
  1502.     
  1503.     info = (TWindow**)GetWRefCon(wind);
  1504.     curField = (**info).curField;
  1505.     fields = (**info).fields;
  1506.     curEdit = (*fields)[curField].edit;
  1507.     quoteStringField = (**info).quoteStringField;
  1508.     tabStops = (**info).tabStops;
  1509.     isTabField = edit == (**info).tabField;
  1510.     isQuoteStringField = edit == quoteStringField;
  1511.     isBodyField = edit == (**info).theTE;
  1512.     isBodyOrSigField = isBodyField || edit == (**info).signatureField;
  1513.     if (deleteCurSel && edit == curEdit) {
  1514.         deletedTextLen = (**curEdit).selEnd - (**curEdit).selStart;
  1515.         if (offset > (**edit).selEnd) offset -= deletedTextLen;
  1516.     } else {
  1517.         deletedTextLen = 0;
  1518.     }
  1519.  
  1520.     if (len == 0) return noErr;
  1521.     if (len >= 0x8000) goto exit1;
  1522.     
  1523.     maxLen = isTabField ? 2 : isQuoteStringField ? 11 : 0x7fff;
  1524.     if (!isBodyField) quote = false;
  1525.     
  1526.     newLen = 0;
  1527.     n = tabStops;
  1528.     for (p = (unsigned char*)*text, pEnd = p + len; p < pEnd; p++) {
  1529.         if (isTabField && (*p < '0' || *p > '9')) goto exit3;
  1530.         if (*p == '\t' && isBodyOrSigField) {
  1531.             newLen += n;
  1532.             n = tabStops;
  1533.         } else if (isPrintable(*p) || (isBodyField && *p == FF)) {
  1534.             newLen++;
  1535.             n--;
  1536.             if (n <= 0) n = tabStops;
  1537.         } else if (*p == CR) {
  1538.             newLen++;
  1539.             n = tabStops;
  1540.         } else if (!isBodyOrSigField) {
  1541.             goto exit3;
  1542.         }
  1543.     }
  1544.     
  1545.     if ((long)(**edit).teLength - deletedTextLen + newLen > maxLen)
  1546.     {
  1547.         if (isTabField || isQuoteStringField) {
  1548.             goto exit4;
  1549.         } else {
  1550.             goto exit1;
  1551.         }
  1552.     }
  1553.     
  1554.     err = MyNewHandle(newLen, &newText);
  1555.     if (err != noErr) goto exit2;
  1556.     
  1557.     n = tabStops;
  1558.     for (p = (unsigned char*)*text, q = (unsigned char*)*newText; p < pEnd; p++) {
  1559.         if (*p == '\t' && isBodyOrSigField) {
  1560.             while (n-- > 0) *q++ = ' ';
  1561.             n = tabStops;
  1562.         } else if (isPrintable(*p) || (isBodyField && *p == FF)) {
  1563.             *q++ = *p;
  1564.             n--;
  1565.             if (n <= 0) n = tabStops;
  1566.         } else if (*p == CR) {
  1567.             *q++ = *p;
  1568.             n = tabStops;
  1569.         }
  1570.     }
  1571.     
  1572.     MyHLock(newText);
  1573.     
  1574.     if (quote) {
  1575.         quoteStringLen = (**quoteStringField).teLength;
  1576.         BlockMoveData(*(**quoteStringField).hText, quoteString, quoteStringLen);
  1577.         p = (unsigned char*)*newText;
  1578.         pEnd = p + newLen;
  1579.         while (p < pEnd) {
  1580.              q = p;
  1581.              while (q < pEnd && *q != CR) q++;
  1582.              if (q - p + quoteStringLen > 80)
  1583.                  Wrap(newText, p - (unsigned char*)*newText, q - (unsigned char*)*newText);
  1584.              p = q+1;
  1585.         }
  1586.         p = (unsigned char*)*newText;
  1587.         pEnd = p + newLen;
  1588.         quotedTextLen = 0;
  1589.         while (p < pEnd) {
  1590.             quotedTextLen += quoteStringLen;
  1591.             q = p;
  1592.             while (q < pEnd && *q != CR) q++;
  1593.             if (q < pEnd) q++;
  1594.             quotedTextLen += q-p;
  1595.             p = q;
  1596.         }
  1597.         if ((long)(**edit).teLength - deletedTextLen + quotedTextLen > 0x7fff) goto exit1;
  1598.         if (deleteCurSel) TEDelete(curEdit);
  1599.         TESetSelect(offset, offset, edit);
  1600.         p = (unsigned char*)*newText;
  1601.         pEnd = p + newLen;
  1602.         while (p < pEnd) {
  1603.             TEInsert(quoteString, quoteStringLen, edit);
  1604.             q = p;
  1605.             while (q < pEnd && *q != CR) q++;
  1606.             if (q < pEnd) q++;
  1607.             TEInsert(p, q-p, edit);
  1608.             p = q;
  1609.         }
  1610.         newLen = quotedTextLen;
  1611.     } else {
  1612.         if (isTabField || isQuoteStringField) {
  1613.             if (deleteCurSel) TEDelete(curEdit);
  1614.             TESetSelect(offset, offset, edit);
  1615.             TEInsert(*newText, newLen, edit);
  1616.         } else {
  1617.             if (deleteCurSel) {
  1618.                 MyTEDelete(curEdit, false, &extraSpaceDeleted);
  1619.                 if (extraSpaceDeleted && edit == curEdit && 
  1620.                     offset > (**edit).selEnd) offset--;
  1621.             }
  1622.             TESetSelect(offset, offset, edit);
  1623.             MyTEPaste(*newText, newLen, edit, maxLen, &extraSpaceAddedInFront);
  1624.             if (extraSpaceAddedInFront) offset++;
  1625.         }
  1626.     }
  1627.     
  1628.     if (deleteCurSel && edit != curEdit) AdjustFieldHeight(wind, curEdit);
  1629.     
  1630.     if (!paste) {
  1631.         if (edit != curEdit) {
  1632.             editIndex = GetFieldIndex(wind, edit);
  1633.             if (editIndex >= 0) ChangeCurField(wind, editIndex);
  1634.         }
  1635.         TESetSelect(offset, offset + newLen, edit); 
  1636.     }
  1637.     
  1638.     MyDisposeHandle(newText);
  1639.     
  1640.     AdjustFieldHeight(wind, edit);
  1641.     (**info).changed = true;
  1642.     
  1643.     return noErr;
  1644.  
  1645. exit1:
  1646.  
  1647.     MyDisposeHandle(newText);
  1648.     ErrorMessageNumber(paste ? kStrScrapTooBigMaxIs32767 :
  1649.         kStrDragTextTooBigMaxIs32767);
  1650.     return userCanceledErr;
  1651.     
  1652. exit2:
  1653.  
  1654.     MyDisposeHandle(newText);
  1655.     return err;
  1656.     
  1657. exit3:
  1658.  
  1659.     MyDisposeHandle(newText);
  1660.     ErrorMessageNumber(paste ? kStrScrapBadChar : 
  1661.         kStrDragTextBadChar);
  1662.     return userCanceledErr;
  1663.     
  1664. exit4:
  1665.  
  1666.     MyDisposeHandle(newText);
  1667.     ErrorMessageNumber(paste ? kStrScrapTooBig :
  1668.         kStrDragTextTooBig);
  1669.     return userCanceledErr;
  1670. }
  1671.  
  1672.  
  1673.  
  1674. /*----------------------------------------------------------------------------
  1675.     FindFieldContainingPoint 
  1676.     
  1677.     Find the field containing a point.
  1678.     
  1679.     Entry:    wind = pointer to message window.
  1680.             where = point in local coords.
  1681.             
  1682.     Exit:    function result = handle to TextEdit field containing the
  1683.                 point, or nil if none.
  1684.             *fieldIndex = index if fields array of field containg the point.
  1685. ----------------------------------------------------------------------------*/
  1686.  
  1687. static TEHandle FindFieldContainingPoint (WindowPtr wind, Point where, short *fieldIndex)
  1688. {
  1689.     TWindow **info;
  1690.     short numFields;
  1691.     TMsgFieldInfo **fields, *f;
  1692.     char state;
  1693.     short i;
  1694.     TEHandle edit;
  1695.     Rect viewRect;
  1696.  
  1697.     info = (TWindow**)GetWRefCon(wind);
  1698.     numFields = (**info).numFields;
  1699.     fields = (**info).fields;
  1700.     state = MyHGetState(fields);
  1701.     MyHLock(fields);
  1702.     for (i = 0, f = *fields; i < numFields; i++, f++) {
  1703.         edit = f->edit;
  1704.         viewRect = (**edit).viewRect;
  1705.         InsetRect(&viewRect, -kTextMargin, 0);
  1706.         if (PtInRect(where, &viewRect)) break;
  1707.     }
  1708.     MyHSetState(fields, state);
  1709.     *fieldIndex = i;
  1710.     return i < numFields ? edit : nil;
  1711. }
  1712.  
  1713.  
  1714.  
  1715. /*----------------------------------------------------------------------------
  1716.     CanAcceptDrag 
  1717.     
  1718.     Figure out whether a dragged object can be accepted by a message window.
  1719.     
  1720.     Entry:    theDrag = drag reference
  1721.             
  1722.     Exit:    function result = true if this object can be accepted by a
  1723.                 message window.
  1724.             *textDrag = true if dragging text, false if dragging groups.
  1725. ----------------------------------------------------------------------------*/
  1726.  
  1727. static Boolean CanAcceptDrag (DragReference theDrag, Boolean *textDrag)
  1728. {
  1729.     unsigned short numItems;
  1730.     ItemReference theItem;
  1731.     FlavorFlags theFlags;
  1732.     OSErr err = noErr;
  1733.     short i;
  1734.     
  1735.     CountDragItems(theDrag, &numItems);
  1736.     for (i = 1; i <= numItems; i++) {
  1737.         GetDragItemReferenceNumber(theDrag, i, &theItem);
  1738.         err = GetFlavorFlags(theDrag, theItem, kNewsWatcherSignature, &theFlags);
  1739.         if (err != noErr) break;
  1740.     }
  1741.     if (err == noErr) {
  1742.         *textDrag = false;
  1743.         return true;
  1744.     }
  1745.     for (i = 1; i <= numItems; i++) {
  1746.         GetDragItemReferenceNumber(theDrag, i, &theItem);
  1747.         err = GetFlavorFlags(theDrag, theItem, 'TEXT', &theFlags);
  1748.         if (err != noErr) return false;
  1749.     }
  1750.     *textDrag = true;
  1751.     return true;
  1752. }
  1753.  
  1754.  
  1755.  
  1756. /*----------------------------------------------------------------------------
  1757.     HandleTracking 
  1758.     
  1759.     Drag Manager tracking handler for message windows.
  1760.     
  1761.     Entry:    message = tracking message from Drag Manager.
  1762.             wind = pointer to message window.
  1763.             handlerRefCon = reference constant (nil).
  1764.             theDrag = drag reference.
  1765.             
  1766.     Exit:    function result = error code.
  1767. ----------------------------------------------------------------------------*/
  1768.  
  1769. static pascal OSErr HandleTracking (DragTrackingMessage message,
  1770.     WindowPtr wind, void *handlerRefCon, DragReference theDrag)
  1771. {
  1772.     TWindow **info;
  1773.     Point where;
  1774.     DragAttributes attributes;
  1775.     static Boolean canAcceptDrag, textDrag;
  1776.     static TEHandle hiliteField;
  1777.     RgnHandle rgn = nil;
  1778.     Boolean leftSender, inSender, inSenderSelection, canAutoScroll;
  1779.     short scrollDelta;
  1780.     static short prevScrollDelta;
  1781.     static long scrollTickCount;
  1782.     Rect viewRect;
  1783.     short fieldIndex, offset;
  1784.     TEHandle destField;
  1785.     static Boolean caretDrawn;
  1786.     static long caretTime;
  1787.     long curTime;
  1788.     ControlHandle vScroll;
  1789.     short val, max;
  1790.     
  1791.     if (gLongOperation || gInDialog) return userCanceledErr;
  1792.     
  1793.     if (gDragErr != noErr) message = dragTrackingLeaveWindow;
  1794.     
  1795.     GetDragAttributes(theDrag, &attributes);
  1796.     leftSender = (attributes & dragHasLeftSenderWindow) != 0;
  1797.     inSender = (attributes & dragInsideSenderWindow) != 0;
  1798.     info = (TWindow**)GetWRefCon(wind);
  1799.     
  1800.     /* Note: Apple's "Drag and Drop Human Interface Guidelines" prohibit autoscrolling
  1801.        unless the source and destination windows are the same and the window is
  1802.        frontmost. I do not like this rule, and have deliberately broken it. I permit
  1803.        autoscrolling inactive windows during drags. */
  1804.     
  1805. /*    canAutoScroll = inSender && wind == FrontWindow();*/
  1806.     canAutoScroll = true;
  1807.     
  1808.     switch (message) {
  1809.     
  1810.         case dragTrackingEnterHandler:
  1811.         
  1812.             break;
  1813.             
  1814.         case dragTrackingEnterWindow:
  1815.         
  1816.             canAcceptDrag = CanAcceptDrag(theDrag, &textDrag);
  1817.             hiliteField = nil;
  1818.             gDestField = nil;
  1819.             gDestFieldOffset = -1;
  1820.             prevScrollDelta = 0;
  1821.             caretDrawn = false;
  1822.             break;
  1823.             
  1824.         case dragTrackingInWindow:
  1825.         
  1826.             if (!canAcceptDrag) break;
  1827.             GetDragMouse(theDrag, &where, nil);
  1828.             GlobalToLocal(&where);
  1829.             destField = FindFieldContainingPoint(wind, where, &fieldIndex);
  1830.             if (!textDrag && destField != (**info).newsgroupsField && 
  1831.                     destField != (**info).followuptoField) destField = nil;
  1832.             if (destField == nil) {
  1833.                 if (caretDrawn) {
  1834.                     DrawTECaret(gDestFieldOffset, gDestField);
  1835.                     caretDrawn = false;
  1836.                 }
  1837.                 if (hiliteField != nil) {
  1838.                     HideDragHilite(theDrag);
  1839.                     hiliteField = nil;
  1840.                 }
  1841.             } else {
  1842.                 if (leftSender && hiliteField != destField) {
  1843.                     if (caretDrawn) {
  1844.                         DrawTECaret(gDestFieldOffset, gDestField);
  1845.                         caretDrawn = false;
  1846.                     }
  1847.                     if (hiliteField != nil) HideDragHilite(theDrag);
  1848.                     rgn = NewRgn();
  1849.                     viewRect = (**destField).viewRect;
  1850.                     if (fieldIndex >= (**info).firstScrollingField) {
  1851.                         InsetRect(&viewRect, -kTextMargin, -3);
  1852.                     } else {
  1853.                         InsetRect(&viewRect, -2, -2);
  1854.                     }
  1855.                     RectRgn(rgn, &viewRect);
  1856.                     ShowDragHilite(theDrag, rgn, true);
  1857.                     DisposeRgn(rgn);
  1858.                     hiliteField = destField;
  1859.                 }
  1860.                 if (textDrag) {
  1861.                     offset = MyTEGetOffset(where, destField);
  1862.                     inSenderSelection = inSender && fieldIndex == (**info).curField && 
  1863.                         (**destField).selStart < offset && offset < (**destField).selEnd;
  1864.                     if (destField == gDestField && offset == gDestFieldOffset) {
  1865.                         if (!inSenderSelection) {
  1866.                             curTime = TickCount();
  1867.                             if (curTime - caretTime >= GetCaretTime()) {
  1868.                                 DrawTECaret(gDestFieldOffset, gDestField);
  1869.                                 caretDrawn = !caretDrawn;
  1870.                                 caretTime = curTime;
  1871.                             }
  1872.                         }
  1873.                     } else {
  1874.                         if (caretDrawn) {
  1875.                             DrawTECaret(gDestFieldOffset, gDestField);
  1876.                             caretDrawn = false;
  1877.                         }
  1878.                         if (!inSenderSelection) {
  1879.                             DrawTECaret(offset, destField);
  1880.                             caretDrawn = true;
  1881.                             caretTime = TickCount();
  1882.                         }
  1883.                     }
  1884.                 }
  1885.             }
  1886.             gDestField = destField;
  1887.             gDestFieldOffset = offset;
  1888.             
  1889.             GetViewRect(wind, &viewRect);
  1890.             vScroll = (**info).vScroll;
  1891.             val = GetControlValue(vScroll);
  1892.             max = GetControlMaximum(vScroll);
  1893.             if (where.v < viewRect.top && val > 0) {
  1894.                 scrollDelta = -1;
  1895.             } else if (where.v > viewRect.bottom && val < max) {
  1896.                 scrollDelta = +1;
  1897.             } else {
  1898.                 scrollDelta = 0;
  1899.             }
  1900.             if (scrollDelta == prevScrollDelta) {
  1901.                 if (scrollDelta != 0 && TickCount() - scrollTickCount < 10) 
  1902.                     scrollDelta = 0;
  1903.             } else {
  1904.                 prevScrollDelta = scrollDelta;
  1905.                 scrollDelta = 0;
  1906.                 scrollTickCount = TickCount();
  1907.             } 
  1908.             if (scrollDelta != 0) {
  1909.                 Scroll(wind, -scrollDelta);
  1910.                 SetControlValue(vScroll, val + scrollDelta);
  1911.             }
  1912.             
  1913.             break;
  1914.             
  1915.         case dragTrackingLeaveWindow:
  1916.         
  1917.             if (caretDrawn) DrawTECaret(gDestFieldOffset, gDestField);
  1918.             if (hiliteField != nil) HideDragHilite(theDrag);
  1919.             hiliteField = nil;
  1920.             gDestField = nil;
  1921.             gDestFieldOffset = -1;
  1922.             prevScrollDelta = 0;
  1923.             caretDrawn = false;
  1924.             break;
  1925.     
  1926.         case dragTrackingLeaveHandler:
  1927.         
  1928.             break;
  1929.             
  1930.     }
  1931.  
  1932.     return noErr;
  1933. }
  1934.  
  1935.  
  1936.  
  1937. /*----------------------------------------------------------------------------
  1938.     MergeTextScrapData 
  1939.     
  1940.     Merge two blocks of text scrap data.
  1941.     
  1942.     Entry:    data1 = handle to first scrap data.
  1943.             data2 = handle to second scrap data.
  1944.             
  1945.     Exit:    function result = error code.
  1946.             data1 = handle to merged scrap data.
  1947. ----------------------------------------------------------------------------*/
  1948.  
  1949. static OSErr MergeTextScrapData (Handle data1, Handle data2)
  1950. {
  1951.     long len1, len2;
  1952.     OSErr err = noErr;
  1953.     
  1954.     len1 = MyGetHandleSize(data1);
  1955.     len2 = MyGetHandleSize(data2);
  1956.     err = MySetHandleSize(data1, len1 + len2 + 1);
  1957.     if (err != noErr) return err;
  1958.     *(*data1 + len1) = ' ';
  1959.     BlockMoveData(*data2, *data1 + len1 + 1, len2);
  1960.     return noErr;
  1961. }
  1962.  
  1963.  
  1964.  
  1965. /*----------------------------------------------------------------------------
  1966.     GetTextDragScrapData 
  1967.     
  1968.     Get the text scrap data at the end of a drag.
  1969.     
  1970.     Entry:    theDrag = drag reference.
  1971.             
  1972.     Exit:    function result = error code.
  1973.             *dragData = handle to drag data.
  1974. ----------------------------------------------------------------------------*/
  1975.  
  1976. static OSErr GetTextDragScrapData (DragReference theDrag, Handle *dragData)
  1977. {
  1978.     unsigned short numItems;
  1979.     ItemReference theItem;
  1980.     OSErr err = noErr;
  1981.     short i;
  1982.     Handle data = nil, itemData = nil;
  1983.     
  1984.     CountDragItems(theDrag, &numItems);
  1985.     for (i = 1; i <= numItems; i++) {
  1986.         err = GetDragItemReferenceNumber(theDrag, i, &theItem);
  1987.         if (err != noErr) goto exit;
  1988.         err = MyGetFlavorDataHandle(theDrag, theItem, 'TEXT', &itemData);
  1989.         if (err != noErr) goto exit;
  1990.         if (i == 1) {
  1991.             data = itemData;
  1992.             itemData = nil;
  1993.         } else {
  1994.             err = MergeTextScrapData(data, itemData);
  1995.             if (err != noErr) goto exit;
  1996.             MyDisposeHandle(itemData);
  1997.             itemData = nil;
  1998.         }
  1999.     }
  2000.     
  2001.     *dragData = data;
  2002.     return noErr;
  2003.     
  2004. exit:
  2005.  
  2006.     MyDisposeHandle(data);
  2007.     MyDisposeHandle(itemData);
  2008.     return err;
  2009. }
  2010.  
  2011.  
  2012.  
  2013. /*----------------------------------------------------------------------------
  2014.     HandleTextDragReceivePostProcessor 
  2015.     
  2016.     Drag Manager receive handler post processor for text drags into
  2017.     message windows.
  2018.     
  2019.     Entry:    gDragDestWindow = pointer to destination window.
  2020.             gFinalDestField = handle to destination TextEdit field.
  2021.             gFinalDestFieldOffset = insertion point offset in destination
  2022.                 TextEdit field.
  2023.             gDragData = handle to drag data.
  2024.             gDragInsertTextQuoted = true to insert text quoted.
  2025.             gDragTextCopy = true if copy, false if move.
  2026.             
  2027.     Exit:    function result = error code.
  2028.     
  2029.     Note: User interaction and network transactions are permitted in
  2030.     this function.
  2031. ----------------------------------------------------------------------------*/
  2032.  
  2033. static OSErr HandleTextDragReceivePostProcessor (void)
  2034. {
  2035.     OSErr err = noErr;
  2036.     GrafPtr port;
  2037.  
  2038.     GetPort(&port);
  2039.     SetPort(gDragDestWindow);
  2040.     err = InsertText(gDragDestWindow, gDragData, MyGetHandleSize(gDragData), 
  2041.         gFinalDestField, gFinalDestFieldOffset, !gDragTextCopy, 
  2042.         gDragInsertTextQuoted, false);
  2043.     MyDisposeHandle(gDragData);
  2044.     gDragData = nil;
  2045.     SetPort(port);
  2046.     return err;
  2047. }
  2048.  
  2049.  
  2050.  
  2051. /*----------------------------------------------------------------------------
  2052.     HandleGroupDragReceivePostProcessor 
  2053.     
  2054.     Drag Manager receive handler post processor for group drags into
  2055.     message windows.
  2056.     
  2057.     Entry:    gDragDestWindow = pointer to destination window.
  2058.             gFinalDestField = handle to destination TextEdit field.
  2059.             gDragData = handle to drag data.
  2060.             
  2061.     Exit:    function result = error code.
  2062.     
  2063.     Note: User interaction and network transactions are permitted in
  2064.     this function.
  2065. ----------------------------------------------------------------------------*/
  2066.  
  2067. static OSErr HandleGroupDragReceivePostProcessor (void)
  2068. {
  2069.     OSErr err = noErr;
  2070.     TWindow **info;
  2071.     Handle text;
  2072.     short textLen;
  2073.     CStr255 groupName;
  2074.     char *p, *pEnd;
  2075.     Boolean fieldIsAllWhiteSpace;
  2076.     GrafPtr port;
  2077.     long dataPos, firstGroupDataPos, i, groupNameLen, lenNewGroups;
  2078.     long numGroups, numNewGroups, numOldGroups, numUnreadPairs;
  2079.     
  2080.     GetPort(&port);
  2081.     SetPort(gDragDestWindow);
  2082.     
  2083.     err = CheckScrapData(gDragData);
  2084.     if (err != noErr) goto exit;
  2085.     
  2086.     info = (TWindow**)GetWRefCon(gDragDestWindow);
  2087.     text = (**gFinalDestField).hText;
  2088.     textLen = (**gFinalDestField).teLength;
  2089.     
  2090.     dataPos = 0;
  2091.     
  2092.     dataPos += sizeof(OSType);                    /* skip subtype */
  2093.     dataPos += 2 * sizeof(unsigned long);        /* skip version numbers */
  2094.     
  2095.     numGroups = *(long*)(*gDragData + dataPos);
  2096.     dataPos += 4;
  2097.     
  2098.     firstGroupDataPos = dataPos;
  2099.     
  2100.     if (numGroups > 50) {
  2101.         ErrorMessageNumber(kStrTooManyGroups);
  2102.         goto exit;
  2103.     }
  2104.  
  2105.     numNewGroups = 0;
  2106.     lenNewGroups = 0;
  2107.     for (i = 0; i < numGroups; i++) {
  2108.         groupNameLen = *(*gDragData + dataPos);
  2109.         dataPos++;
  2110.         BlockMoveData(*gDragData + dataPos, groupName, groupNameLen);
  2111.         dataPos += groupNameLen;
  2112.         dataPos = ((dataPos + 3) >> 2) << 2;
  2113.         *(groupName + groupNameLen) = 0;
  2114.         if (!MyIsASubstringHandle(text, groupName)) {
  2115.             numNewGroups++;
  2116.             lenNewGroups += groupNameLen + 2;
  2117.         }
  2118.         dataPos += 3*sizeof(long);
  2119.         numUnreadPairs = *(long*)(*gDragData + dataPos);
  2120.         dataPos += sizeof(long);
  2121.         dataPos += numUnreadPairs * 2 * sizeof(long);
  2122.     }
  2123.     
  2124.     if (numNewGroups == 0) goto exit;
  2125.     
  2126.     numOldGroups = 0;
  2127.     p = *text;
  2128.     pEnd = p + textLen;
  2129.     while (p < pEnd && isLWSP(*p)) p++;
  2130.     fieldIsAllWhiteSpace = p >= pEnd;
  2131.     while (p < pEnd) {
  2132.         if (*p == ' ' || *p == ',') {
  2133.             while (p < pEnd && (*p == ' ' || *p == ',')) p++;
  2134.             if (p < pEnd) numOldGroups++;
  2135.         }
  2136.         p++;
  2137.     }
  2138.     if (!fieldIsAllWhiteSpace) numOldGroups++;
  2139.     
  2140.     if (numNewGroups + numOldGroups > 5) {
  2141.         err = TooManyGroupsDialog(numNewGroups + numOldGroups);
  2142.         if (err != noErr) goto exit;
  2143.     }
  2144.     
  2145.     if (textLen + lenNewGroups > 0x7fff) {
  2146.         ErrorMessageNumber(kStrGroupDragTooBigMaxIs32767);
  2147.         err = userCanceledErr;
  2148.         goto exit;
  2149.     }
  2150.     
  2151.     dataPos = firstGroupDataPos;
  2152.     TESetSelect(0x7fff, 0x7fff, gFinalDestField);
  2153.     for (i = 0; i < numGroups; i++) {
  2154.         groupNameLen = *(*gDragData + dataPos);
  2155.         dataPos++;
  2156.         BlockMoveData(*gDragData + dataPos, groupName, groupNameLen);
  2157.         dataPos += groupNameLen;
  2158.         dataPos = ((dataPos + 3) >> 2) << 2;
  2159.         *(groupName + groupNameLen) = 0;
  2160.         if (!MyIsASubstringHandle(text, groupName)) {
  2161.             if (!fieldIsAllWhiteSpace) TEInsert(", ", 2, gFinalDestField);
  2162.             fieldIsAllWhiteSpace = false;
  2163.             TEInsert(groupName, groupNameLen, gFinalDestField);
  2164.         }
  2165.         dataPos += 3*sizeof(long);
  2166.         numUnreadPairs = *(long*)(*gDragData + dataPos);
  2167.         dataPos += sizeof(long);
  2168.         dataPos += numUnreadPairs * 2 * sizeof(long);
  2169.     }
  2170.     
  2171.     (**info).changed = true;
  2172.     
  2173.     AdjustFieldHeight(gDragDestWindow, gFinalDestField);
  2174.  
  2175. exit:
  2176.  
  2177.     MyDisposeHandle(gDragData);
  2178.     gDragData = nil;
  2179.     SetPort(port);
  2180.     return err;
  2181. }
  2182.  
  2183.  
  2184.  
  2185. /*----------------------------------------------------------------------------
  2186.     HandleReceive 
  2187.     
  2188.     Drag Manager receive handler for message windows.
  2189.     
  2190.     Entry:    wind = pointer to message window.
  2191.             handlerRefCon = reference constant (nil).
  2192.             theDrag = drag reference.
  2193.             
  2194.     Exit:    function result = error code.
  2195.     
  2196.     Note: No user interaction or network transactions are permitted in
  2197.     this function.
  2198. ----------------------------------------------------------------------------*/
  2199.  
  2200. static pascal OSErr HandleReceive (WindowPtr wind, void *handlerRefCon, 
  2201.     DragReference theDrag)
  2202. {
  2203.     Boolean textDrag;
  2204.     DragAttributes attributes;
  2205.     short mouseDownModifiers, mouseUpModifiers;
  2206.     TWindow **info;
  2207.     TMsgFieldInfo **fields;
  2208.     short curField, selStart, selEnd;
  2209.     TEHandle edit;
  2210.  
  2211.     if (gLongOperation || gInDialog || gDragErr != noErr ||
  2212.         gDestField == nil || !CanAcceptDrag(theDrag, &textDrag)) goto exit;
  2213.     
  2214.     gDragDestWindow = wind;
  2215.     gFinalDestField = gDestField;
  2216.     
  2217.     if (textDrag) {
  2218.         gFinalDestFieldOffset = gDestFieldOffset;
  2219.         gDragErr = GetTextDragScrapData(theDrag, &gDragData);
  2220.         if (gDragErr != noErr) goto exit;
  2221.         gDragTextCopy = true;
  2222.         GetDragAttributes(theDrag, &attributes);
  2223.         GetDragModifiers(theDrag, nil, &mouseDownModifiers, &mouseUpModifiers);
  2224.         gDragInsertTextQuoted = (mouseDownModifiers & shiftKey) != 0 ||
  2225.                 (mouseUpModifiers & shiftKey) != 0;
  2226.         if ((attributes & dragInsideSenderWindow) != 0) {
  2227.             gDragTextCopy = (mouseDownModifiers & optionKey) != 0 ||
  2228.                 (mouseUpModifiers & optionKey) != 0;
  2229.             info = (TWindow**)GetWRefCon(gDragDestWindow);
  2230.             fields = (**info).fields;
  2231.             curField = (**info).curField;
  2232.             edit = (*fields)[curField].edit;
  2233.             selStart = (**edit).selStart;
  2234.             selEnd = (**edit).selEnd;
  2235.             if (edit == gFinalDestField) {
  2236.                 if ((gDragTextCopy && selStart < gFinalDestFieldOffset && 
  2237.                         gFinalDestFieldOffset < selEnd) ||
  2238.                     (!gDragTextCopy && selStart <= gFinalDestFieldOffset &&
  2239.                         gFinalDestFieldOffset <= selEnd))
  2240.                 {
  2241.                     gDragErr = userCanceledErr;
  2242.                     goto exit;
  2243.                 }
  2244.             }
  2245.         }
  2246.         gDragPostProcessor = HandleTextDragReceivePostProcessor;
  2247.     } else {
  2248.         gDragErr = GetGroupDragScrapData(theDrag, &gDragData);
  2249.         if (gDragErr != noErr) goto exit;
  2250.         gDragPostProcessor = HandleGroupDragReceivePostProcessor;
  2251.     }
  2252.     
  2253.     return noErr;
  2254.     
  2255. exit:
  2256.  
  2257.     HandleTracking(dragTrackingLeaveWindow, wind, handlerRefCon, theDrag);
  2258.     return dragNotAcceptedErr;
  2259. }
  2260.  
  2261.  
  2262.  
  2263. /*----------------------------------------------------------------------------
  2264.     MakeNewWindow 
  2265.     
  2266.     Create and initialize a new message window.
  2267.             
  2268.     Exit:    function result = error code.
  2269.             *theWindow = pointer to new window.
  2270. ----------------------------------------------------------------------------*/
  2271.  
  2272. static OSErr MakeNewWindow (WindowPtr *theWindow)
  2273. {
  2274.     short width, height;
  2275.     WindowPtr wind = nil;
  2276.     GrafPtr port;
  2277.     TWindow **info;
  2278.     Rect r;
  2279.     TEHandle signatureField, tabField, quoteStringField;
  2280.     short sigLen, panelHeight, optionsPanelHeight, lineHeight, lineHeightDiv2;
  2281.     short iconPanelHeight;
  2282.     OSErr err = noErr;
  2283.     ControlHandle vScroll, sendButton, tabCheckbox, wrapCheckbox;
  2284.     Str255 title;
  2285.     Str255 tabStopsStr, quoteStr, contrlTitle;
  2286.     CStr255 tabStopsValueStr;
  2287.     short optionsDeltaV, top, tabCheckboxLength, wrapCheckboxLength;
  2288.     short optionsSecondColLeft, sendButtonTop;
  2289.     TMsgFieldInfo **fields;
  2290.     
  2291.     MyICReadSharedPrefs(kICSignature);
  2292.     MyICReadSharedPrefs(kICMailHeaders);
  2293.     MyICReadSharedPrefs(kICNewsHeaders);
  2294.     MyICReadSharedPrefs(kICScreenFont);
  2295.     
  2296.     GetPort(&port);
  2297.  
  2298.     GetPString(kStrNoSubject, title);
  2299.     err = CreateNewWindow(kMessage, title, gPrefs.textFont, gPrefs.textSize, &wind);
  2300.     if (err != noErr) return err;
  2301.     SetPort(wind);
  2302.     
  2303.     info = (TWindow**)GetWRefCon(wind);
  2304.  
  2305.     lineHeight = GetFontLineHeight(wind);
  2306.     lineHeightDiv2 = lineHeight >> 1;
  2307.     optionsDeltaV = lineHeight + 10;
  2308.     optionsPanelHeight = 2*optionsDeltaV + 6;
  2309.     optionsPanelHeight = (optionsPanelHeight + lineHeight - 1) / lineHeight * lineHeight;
  2310.     (**info).optionsPanelHeight = optionsPanelHeight;
  2311.     
  2312.     if (gPrefs.showLabelsUnderIcons) {
  2313.         (**info).showLabels = true;
  2314.         iconPanelHeight = 54;
  2315.     } else {
  2316.         iconPanelHeight = 40;
  2317.     }
  2318.     panelHeight = iconPanelHeight;
  2319.     if (gPrefs.showMsgDetails) panelHeight += optionsPanelHeight;
  2320.     (**info).panelHeight = panelHeight;
  2321.     (**info).iconPanelHeight = iconPanelHeight;
  2322.     
  2323.     top = iconPanelHeight + ((optionsPanelHeight - 2*optionsDeltaV - 3) >> 1);
  2324.     tabCheckbox = GetNewControl(kTabCheckboxID, wind);
  2325.     CopyPascalString(contrlTitle, (**tabCheckbox).contrlTitle);
  2326.     tabCheckboxLength = 2*lineHeight + StringWidth(contrlTitle);
  2327.     r.top = top;
  2328.     r.left = kOptionsPanelLeftMargin;
  2329.     r.bottom = r.top + optionsDeltaV;
  2330.     r.right = r.left + tabCheckboxLength;
  2331.     (**tabCheckbox).contrlRect = r;
  2332.     SetControlValue(tabCheckbox, gPrefs.tabEnabled ? 1 : 0);
  2333.     if (!gPrefs.showMsgDetails) HideControl(tabCheckbox);
  2334.     (**info).tabCheckbox = tabCheckbox;
  2335.     (**info).tabEnabled = gPrefs.tabEnabled;
  2336.     
  2337.     wrapCheckbox = GetNewControl(kWrapCheckboxID, wind);
  2338.     CopyPascalString(contrlTitle, (**wrapCheckbox).contrlTitle);
  2339.     wrapCheckboxLength = 2*lineHeight + StringWidth(contrlTitle);
  2340.     r.top = top + optionsDeltaV;
  2341.     r.left = kOptionsPanelLeftMargin;
  2342.     r.bottom = r.top + optionsDeltaV;
  2343.     r.right = r.left + wrapCheckboxLength;
  2344.     (**wrapCheckbox).contrlRect = r;
  2345.     SetControlValue(wrapCheckbox, gPrefs.wrapOnSend ? 1 : 0);
  2346.     if (!gPrefs.showMsgDetails) HideControl(wrapCheckbox);
  2347.     (**info).wrapCheckbox = wrapCheckbox;
  2348.     (**info).wrapOnSend = gPrefs.wrapOnSend;
  2349.     
  2350.     optionsSecondColLeft = kOptionsPanelLeftMargin + 15 + 
  2351.         (tabCheckboxLength < wrapCheckboxLength ? wrapCheckboxLength : tabCheckboxLength);
  2352.     GetPString(kStrTabStops, tabStopsStr);
  2353.     r.top = top + 5;
  2354.     r.bottom = r.top + lineHeight;
  2355.     r.left = optionsSecondColLeft + StringWidth(tabStopsStr) + 2;
  2356.     r.right = r.left + 3*CharWidth('9');
  2357.     tabField = TENew(&r, &r);
  2358.     sprintf(tabStopsValueStr, "%d", gPrefs.tabStops);
  2359.     err = MyTESetText(tabStopsValueStr, strlen(tabStopsValueStr), tabField);
  2360.     if (err != noErr) goto exit;
  2361.     (**info).tabField = tabField;
  2362.     (**info).tabStops = gPrefs.tabStops;
  2363.     
  2364.     GetPString(kStrQuoteString, quoteStr);
  2365.     r.top = top + 5 + optionsDeltaV;
  2366.     r.bottom = r.top + lineHeight;
  2367.     r.left = optionsSecondColLeft + StringWidth(quoteStr) + 2;
  2368.     r.right = r.left + 12*CharWidth('9');
  2369.     quoteStringField = TENew(&r, &r);
  2370.     err = MyTESetText(gPrefs.quoteString, strlen(gPrefs.quoteString), quoteStringField);
  2371.     if (err != noErr) goto exit;
  2372.     (**info).quoteStringField = quoteStringField;
  2373.     
  2374.     width = MinWidth(wind);
  2375.     height = MinHeight(wind);
  2376.     PositionNewWindow(wind, width, height);
  2377.     
  2378.     SetRect(&r, width-15, panelHeight-1, width+1, height-14); 
  2379.     vScroll = NewControl(wind, &r, "\p", true, 0, 0, 0, scrollBarProc, 1);
  2380.     (**info).vScroll = vScroll;
  2381.  
  2382.     sendButton = GetNewControl(kSendButtonID, wind);
  2383.     sendButtonTop = (**info).showLabels ? kSendButtonTopLabels : kSendButtonTopNoLabels;
  2384.     MoveControl(sendButton, width-70, sendButtonTop);
  2385.     (**info).sendButton = sendButton;
  2386.  
  2387.     (**info).selfIcon = gPrefs.copySelf;
  2388.     (**info).showDetails = gPrefs.showMsgDetails;
  2389.     
  2390.     (**info).theTE = MyTENew();
  2391.     (**info).newsgroupsField = MyTENew();
  2392.     (**info).toField = MyTENew();
  2393.     (**info).subjectField = MyTENew();
  2394.     (**info).ccField = MyTENew();
  2395.     (**info).bccField = MyTENew();
  2396.     (**info).replytoField = MyTENew();
  2397.     (**info).followuptoField = MyTENew();
  2398.     (**info).keywordsField = MyTENew();
  2399.     (**info).distributionField = MyTENew();
  2400.          
  2401.     (**info).extraNewsField = MyTENew();
  2402.     TEInsert(gPrefs.extraNewsHdrLines, strlen(gPrefs.extraNewsHdrLines), 
  2403.         (**info).extraNewsField);
  2404.     
  2405.     (**info).extraMailField = MyTENew();
  2406.     TEInsert(gPrefs.extraMailHdrLines, strlen(gPrefs.extraMailHdrLines), 
  2407.         (**info).extraMailField);
  2408.     
  2409.     signatureField = MyTENew();
  2410.     sigLen = strlen(gPrefs.signature);
  2411.     if (sigLen > 0) {
  2412.         if (gPrefs.addSigBlankLine) TEInsert(CRSTR, 1, signatureField);
  2413.         if (gPrefs.addSigSeparatorLine) TEInsert("-- \r", 4, signatureField);
  2414.         TEInsert(gPrefs.signature, sigLen, signatureField);
  2415.     }
  2416.     (**info).signatureField = signatureField;
  2417.     
  2418.     err = MyNewHandle(kMaxFields * sizeof(TMsgFieldInfo), &fields);
  2419.     if (err != noErr) goto exit;
  2420.     (**info).fields = fields;
  2421.     
  2422.     if (gHaveDragMgr) {
  2423.         err = InstallTrackingHandler(gHandleTrackingUPP, wind, nil);
  2424.         if (err != noErr) goto exit;
  2425.         err = InstallReceiveHandler(gHandleReceiveUPP, wind, nil);
  2426.         if (err != noErr) goto exit;
  2427.     }
  2428.  
  2429.     *theWindow = wind;
  2430.     SetPort(port);
  2431.     return noErr;
  2432.     
  2433. exit:
  2434.  
  2435.     DoClose(wind);
  2436.     SetPort(port);
  2437.     return err;
  2438. }
  2439.  
  2440.  
  2441.  
  2442. /*----------------------------------------------------------------------------
  2443.     BuildReplyBody 
  2444.     
  2445.     Build the body of a reply message.
  2446.             
  2447.     Entry:    text = handle to full article text.
  2448.             quoteText = handle to text to be quoted, or nil to quote the
  2449.                 full text.
  2450.             start = offset in quoteText of beginning of text to
  2451.                 be quoted.
  2452.             end = offset in quoteText of end of text to be quoted.
  2453.             messageid = handle to article message id.
  2454.             from = handle to message "from" header line.
  2455.             body = handle.
  2456.             
  2457.     Exit:    function result = error code.
  2458.             body = handle to reply body.
  2459. ----------------------------------------------------------------------------*/
  2460.  
  2461. static OSErr BuildReplyBody (Handle text, Handle quoteText, long start, 
  2462.     long end, Handle messageid, Handle from, Handle body)
  2463. {
  2464.     long len, messageidLen, fromLen, quoteStart;
  2465.     long len1, len2, len3;
  2466.     char *q;
  2467.     OSErr err = noErr;
  2468.     CStr255 quoteStr1, quoteStr2, quoteStr3;
  2469.     
  2470.     if (quoteText == nil) {
  2471.         quoteText = text;
  2472.         start = FindBody(quoteText);
  2473.         end = MyGetHandleSize(text);
  2474.     }
  2475.  
  2476.     messageidLen = messageid == nil ? 0 : MyGetHandleSize(messageid);
  2477.     fromLen = from == nil ? 0 : MyGetHandleSize(from);
  2478.     GetCString(kStrQuoteStr1, quoteStr1);
  2479.     GetCString(kStrQuoteStr2, quoteStr2);
  2480.     GetCString(kStrQuoteStr3, quoteStr3);
  2481.     len1 = strlen(quoteStr1);
  2482.     len2 = strlen(quoteStr2);
  2483.     len3 = strlen(quoteStr3);
  2484.     quoteStart = len1 + messageidLen + len2 + fromLen + len3 + 2;
  2485.     len = quoteStart + end - start;
  2486.  
  2487.     err = MySetHandleSize(body, len);    
  2488.     if (err != noErr) return err;
  2489.      
  2490.     q = *body;
  2491.     BlockMoveData(quoteStr1, q, len1);
  2492.     q += len1;
  2493.     if (messageid != nil) {
  2494.         BlockMoveData(*messageid, q, messageidLen);
  2495.         q += messageidLen;
  2496.     }
  2497.     BlockMoveData(quoteStr2, q, len2);
  2498.     q += len2;
  2499.     if (from != nil) {
  2500.         BlockMoveData(*from, q, fromLen);
  2501.         q += fromLen;
  2502.     }
  2503.     BlockMoveData(quoteStr3, q, len3);
  2504.     q += len3;
  2505.     *q++ = CR;
  2506.     *q++ = CR;
  2507.     BlockMoveData(*quoteText + start, q, end - start);
  2508.     
  2509.     err = QuoteText(body, quoteStart, len);
  2510.     if (err != noErr) return err;
  2511.     
  2512.     len = MyGetHandleSize(body);
  2513.     if (len > 0x7fff) {
  2514.         MySetHandleSize(body, 0x7fff);
  2515.         ErrorMessageNumber(kStrTrunc);
  2516.     }
  2517.     
  2518.     return noErr;
  2519. }
  2520.  
  2521.  
  2522.  
  2523. /*----------------------------------------------------------------------------
  2524.     FoldReferences 
  2525.     
  2526.     Fold the "References" header line.
  2527.             
  2528.     Entry:    references = handle to references.
  2529.             
  2530.     Exit:    function result = error code.
  2531.             references = handle to folded references.
  2532. ----------------------------------------------------------------------------*/
  2533.  
  2534. static OSErr FoldReferences (Handle references)
  2535. {
  2536.     long numFolds = 0;
  2537.     char *p, *pEnd, *q;
  2538.     long len, newLen;
  2539.     OSErr err = noErr;
  2540.     
  2541.     /* Unfortunately, INN 1.4 doesn't understand folded References header 
  2542.        lines. Also, Rich Salz recommended limiting them to 1K. So this
  2543.        function just truncates to 1K instead of doing folding. The code 
  2544.        is still present, just inactive, so if we ever want to turn it on
  2545.        we can. */
  2546.        
  2547.     {
  2548.         len = MyGetHandleSize(references);
  2549.         if (len < 1000) return noErr;
  2550.         pEnd = *references + len;
  2551.         p = pEnd - 999;
  2552.         while (p < pEnd && *p != '<') p++;
  2553.         newLen = pEnd - p;
  2554.         BlockMoveData(p, *references, newLen);
  2555.         MySetHandleSize(references, newLen);
  2556.         return noErr;
  2557.     }
  2558.     
  2559.     len = MyGetHandleSize(references);
  2560.     
  2561.     p = *references;
  2562.     pEnd = *references + len;
  2563.     while (p < pEnd) {
  2564.         if (isLWSP(*p)) {
  2565.             numFolds++;
  2566.             p++;
  2567.             while (p < pEnd && isLWSP(*p)) p++;
  2568.         } else {
  2569.             p++;
  2570.         }
  2571.     }
  2572.     
  2573.     newLen = len + 3*numFolds;
  2574.     err = MySetHandleSize(references, newLen);
  2575.     if (err != noErr) return err;
  2576.     
  2577.     BlockMoveData(*references, *references + newLen - len, len);
  2578.     
  2579.     p = *references + newLen - len;
  2580.     pEnd = *references + newLen;
  2581.     q = *references;
  2582.     while (p < pEnd) {
  2583.         if (isLWSP(*p)) {
  2584.             *q++= CR;
  2585.             *q++ = ' ';
  2586.             *q++ = ' ';
  2587.             *q++ = ' ';
  2588.             p++;
  2589.             while (p < pEnd && isLWSP(*p)) p++;
  2590.         } else {
  2591.             *q++ = *p++;
  2592.         }
  2593.     }
  2594.     
  2595.     MySetHandleSize(references, q - *references);
  2596.     return noErr;
  2597. }
  2598.  
  2599.  
  2600.  
  2601. /*----------------------------------------------------------------------------
  2602.     OpenReplyWindow 
  2603.     
  2604.     Open a reply window.
  2605.             
  2606.     Entry:    text = handle to full article text.
  2607.             quoteText = handle to text to be quoted, or nil to quote the
  2608.                 full text.
  2609.             start = offset in quoteText of beginning of text to
  2610.                 be quoted.
  2611.             end = offset in quoteText of end of text to be quoted.
  2612.             reverseIcons = true to reverse the normal settings of the news
  2613.                 and mail icons.
  2614.             
  2615.     Exit:    function result = error code.
  2616. ----------------------------------------------------------------------------*/
  2617.  
  2618. OSErr OpenReplyWindow (Handle text, Handle quoteText, long start, long end,
  2619.     Boolean reverseIcons)
  2620. {
  2621.     WindowPtr wind = nil;
  2622.     TWindow **info;
  2623.     Handle newsgroups = nil;
  2624.     Handle subject = nil; 
  2625.     Handle followupto = nil; 
  2626.     Handle from = nil; 
  2627.     Handle replyto = nil; 
  2628.     Handle references = nil; 
  2629.     Handle messageid = nil;
  2630.     Handle distribution = nil;
  2631.     OSErr err = noErr;
  2632.     Boolean followuptoPoster = false;
  2633.     long subjectLen, newSubjectLen;
  2634.     long refLen, idLen, newRefLen, oldReLen;
  2635.     Handle newReferences;
  2636.     TEHandle theTE, newsgroupsField, toField, subjectField, distributionField;
  2637.     Handle hText;
  2638.     short i, iEnd;
  2639.     GrafPtr port;
  2640.     
  2641.     GetPort(&port);
  2642.  
  2643.     err = FindHeaderHandle(text, "Newsgroups", &newsgroups);
  2644.     if (err != noErr) goto exit;
  2645.     err = FindHeaderHandle(text, "Subject", &subject);
  2646.     if (err != noErr) goto exit;
  2647.     err = FindHeaderHandle(text, "Followup-To", &followupto);
  2648.     if (err != noErr) goto exit;
  2649.     err = FindHeaderHandle(text, "From", &from);
  2650.     if (err != noErr) goto exit;
  2651.     err = FindHeaderHandle(text, "Reply-To", &replyto);
  2652.     if (err != noErr) goto exit;
  2653.     err = FindHeaderHandle(text, "References", &references);
  2654.     if (err != noErr) goto exit;
  2655.     err = FindHeaderHandle(text, "Message-ID", &messageid);
  2656.     if (err != noErr) goto exit;
  2657.     err = FindHeaderHandle(text, "Distribution", &distribution);
  2658.     if (err != noErr) goto exit;
  2659.     
  2660.     err = MakeNewWindow(&wind);
  2661.     if (err != noErr) goto exit;
  2662.     SetPort(wind);
  2663.     info = (TWindow**)GetWRefCon(wind);
  2664.     
  2665.     if (followupto != nil && MyGetHandleSize(followupto) == 6) {
  2666.         followuptoPoster = MyStrNEqual(*followupto, "poster", 6);
  2667.     }
  2668.     
  2669.     if (followuptoPoster) {
  2670.         (**info).newsIcon = false;
  2671.         (**info).mailIcon = true;
  2672.     } else if (reverseIcons) {
  2673.         (**info).newsIcon = !gPrefs.replyPost;
  2674.         (**info).mailIcon = !gPrefs.replyEmail;
  2675.     } else {
  2676.         (**info).newsIcon = gPrefs.replyPost;
  2677.         (**info).mailIcon = gPrefs.replyEmail;
  2678.     }
  2679.     
  2680.     newsgroupsField = (**info).newsgroupsField;
  2681.     if (!followuptoPoster && followupto != nil) {
  2682.         MyDisposeHandle((**newsgroupsField).hText);
  2683.         (**newsgroupsField).hText = followupto;
  2684.         followupto = nil;
  2685.     } else if (newsgroups != nil) {
  2686.         MyDisposeHandle((**newsgroupsField).hText);
  2687.         (**newsgroupsField).hText = newsgroups;
  2688.         newsgroups = nil;
  2689.     }
  2690.     TECalText(newsgroupsField);
  2691.     i = 0;
  2692.     iEnd = (**newsgroupsField).teLength;
  2693.     hText = (**newsgroupsField).hText;
  2694.     while (i < iEnd) {
  2695.         if (*(*hText+i) == ',' && *(*hText+i+1) != ' ') {
  2696.             (**newsgroupsField).selStart = (**newsgroupsField).selEnd = i+1;
  2697.             TEKey(' ', newsgroupsField);
  2698.             iEnd++;
  2699.             i++;
  2700.         }
  2701.         i++;
  2702.     }
  2703.     
  2704.     toField = (**info).toField;
  2705.     if (replyto != nil) {
  2706.         MyDisposeHandle((**toField).hText);
  2707.         (**toField).hText = replyto;
  2708.         replyto = nil;
  2709.     } else if (from != nil) {
  2710.         MyDisposeHandle((**toField).hText);
  2711.         (**toField).hText = from;
  2712.         from = nil;
  2713.     }
  2714.     
  2715.     distributionField = (**info).distributionField;
  2716.     if (distribution != nil) {
  2717.         MyDisposeHandle((**distributionField).hText);
  2718.         (**distributionField).hText = distribution;
  2719.         distribution = nil;
  2720.     }
  2721.     
  2722.     subjectField = (**info).subjectField;
  2723.     
  2724.     if (subject != nil) {
  2725.         subjectLen = MyGetHandleSize(subject);
  2726.         oldReLen = ParseRe(*subject, subjectLen);
  2727.         newSubjectLen = subjectLen - oldReLen + 4;
  2728.         if (newSubjectLen > subjectLen) {
  2729.             err = MySetHandleSize(subject, newSubjectLen);
  2730.             if (err != noErr) goto exit;
  2731.         }
  2732.         BlockMoveData(*subject + oldReLen, *subject + 4, subjectLen - oldReLen);
  2733.         if (newSubjectLen < subjectLen) MySetHandleSize(subject, newSubjectLen);
  2734.         subjectLen = newSubjectLen;
  2735.         BlockMoveData("Re: ", *subject, 4);
  2736.         MyDisposeHandle((**subjectField).hText);
  2737.         (**subjectField).hText = subject;
  2738.         subject = nil;
  2739.     } else {
  2740.         err = MyTESetText("Re: ", 4, subjectField);
  2741.         if (err != noErr) goto exit;
  2742.     }
  2743.     ChangeSubject(wind);
  2744.     
  2745.     refLen = references == nil ? 0 : MyGetHandleSize(references);
  2746.     idLen = messageid == nil ? 0 : MyGetHandleSize(messageid);
  2747.     if (refLen > 0 || idLen > 0) {
  2748.         newRefLen = refLen + idLen;
  2749.         if (refLen > 0 && idLen > 0) newRefLen++;
  2750.         err = MyNewHandle(newRefLen, &newReferences);
  2751.         if (err != noErr) goto exit;
  2752.         if (refLen > 0) {
  2753.             BlockMoveData(*references, *newReferences, refLen);
  2754.             if (idLen > 0) {
  2755.                 *(*newReferences + refLen) = ' ';
  2756.                 BlockMoveData(*messageid, *newReferences + refLen + 1, idLen);
  2757.             }
  2758.         } else {
  2759.             BlockMoveData(*messageid, *newReferences, idLen);
  2760.         }
  2761.         err = FoldReferences(newReferences);
  2762.         if (err != noErr) goto exit;
  2763.         (**info).references = newReferences;
  2764.     }
  2765.     
  2766.     theTE = (**info).theTE;
  2767.     err = BuildReplyBody(text, quoteText, start, end, 
  2768.         messageid, (**toField).hText, (**theTE).hText);
  2769.     if (err != noErr) goto exit;
  2770.  
  2771.     MyDisposeHandle(newsgroups);
  2772.     MyDisposeHandle(subject);
  2773.     MyDisposeHandle(followupto);
  2774.     MyDisposeHandle(from);
  2775.     MyDisposeHandle(replyto);
  2776.     MyDisposeHandle(references);
  2777.     MyDisposeHandle(messageid);
  2778.     MyDisposeHandle(distribution);
  2779.  
  2780.     InitFieldInfo(wind);
  2781.     err = DoZoom(wind, inZoomOut);
  2782.     if (err != noErr) goto exit;
  2783.     
  2784.     (**info).isFollowup = true;
  2785.  
  2786.     MyShowWindow(wind);
  2787.     SetPort(port);
  2788.     return noErr;
  2789.     
  2790. exit:
  2791.  
  2792.     MyDisposeHandle(newsgroups);
  2793.     MyDisposeHandle(subject);
  2794.     MyDisposeHandle(followupto);
  2795.     MyDisposeHandle(from);
  2796.     MyDisposeHandle(replyto);
  2797.     MyDisposeHandle(references);
  2798.     MyDisposeHandle(messageid);
  2799.     MyDisposeHandle(distribution);
  2800.     DoClose(wind);
  2801.     SetPort(port);
  2802.     return err;
  2803. }
  2804.  
  2805.  
  2806.  
  2807. /*----------------------------------------------------------------------------
  2808.     BuildForwardBody 
  2809.     
  2810.     Build the body of a forward  message.
  2811.             
  2812.     Entry:    text = handle to full article text.
  2813.             quoteText = handle to text to be quoted, or nil to quote the
  2814.                 full text.
  2815.             start = offset in quoteText of beginning of text to
  2816.                 be quoted.
  2817.             end = offset in quoteText of end of text to be quoted.
  2818.             body = handle.
  2819.             
  2820.     Exit:    function result = error code.
  2821.             body = handle to message body.
  2822. ----------------------------------------------------------------------------*/
  2823.  
  2824. static OSErr BuildForwardBody (Handle text, Handle quoteText, long start, 
  2825.     long end, Handle body)
  2826. {
  2827.     long len, headerLen;
  2828.     OSErr err = noErr;
  2829.     
  2830.     headerLen = FindBody(text);
  2831.     
  2832.     if (quoteText == nil) {
  2833.         len = MyGetHandleSize(text);
  2834.     } else {
  2835.         len = headerLen + end - start;
  2836.     }
  2837.     
  2838.     err = MySetHandleSize(body, len);
  2839.     if (err != noErr) return err;
  2840.     
  2841.     if (quoteText == nil) {
  2842.         BlockMoveData(*text, *body, len);
  2843.     } else {
  2844.         BlockMoveData(*text, *body, headerLen);
  2845.         BlockMoveData(*quoteText + start, *body + headerLen, end - start);
  2846.     }
  2847.     
  2848.     err = QuoteText(body, 0, len);
  2849.     if (err != noErr) return err;
  2850.     
  2851.     len = MyGetHandleSize(body);
  2852.     if (len > 0x7fff) {
  2853.         MySetHandleSize(body, 0x7fff);
  2854.         ErrorMessageNumber(kStrTrunc);
  2855.     }
  2856.     
  2857.     return noErr;
  2858. }
  2859.  
  2860.  
  2861.  
  2862. /*----------------------------------------------------------------------------
  2863.     OpenForwardWindow 
  2864.     
  2865.     Open a forward window.
  2866.             
  2867.     Entry:    text = handle to full article text.
  2868.             quoteText = handle to text to be quoted, or nil to quote the
  2869.                 full text.
  2870.             start = offset in quoteText of beginning of text to
  2871.                 be quoted.
  2872.             end = offset in quoteText of end of text to be quoted.
  2873.             mailToSelf = true to mail the forwarded message to self.
  2874.             
  2875.     Exit:    function result = error code.
  2876. ----------------------------------------------------------------------------*/
  2877.  
  2878. OSErr OpenForwardWindow (Handle text, Handle quoteText, long start, long end,
  2879.     Boolean mailToSelf)
  2880. {
  2881.     WindowPtr wind = nil;
  2882.     TWindow **info;
  2883.     Handle subject = nil; 
  2884.     OSErr err = noErr;
  2885.     TEHandle subjectField, theTE;
  2886.     GrafPtr port;
  2887.     
  2888.     GetPort(&port);
  2889.     
  2890.     err = MakeNewWindow(&wind);
  2891.     if (err != noErr) return err;
  2892.     SetPort(wind);
  2893.     info = (TWindow**)GetWRefCon(wind);
  2894.  
  2895.     (**info).newsIcon = false;
  2896.     if (mailToSelf) {
  2897.         (**info).selfIcon = true;
  2898.     } else {
  2899.         (**info).mailIcon = true;
  2900.     }
  2901.  
  2902.     err = FindHeaderHandle(text, "Subject", &subject);
  2903.     if (err != noErr) goto exit;
  2904.     subjectField = (**info).subjectField;
  2905.     if (subject != nil) {
  2906.         MyDisposeHandle((**subjectField).hText);
  2907.         (**subjectField).hText = subject;
  2908.         subject = nil;
  2909.         ChangeSubject(wind);
  2910.     }
  2911.     
  2912.     theTE = (**info).theTE;
  2913.     err = BuildForwardBody(text, quoteText, start, end, (**theTE).hText);
  2914.     if (err != noErr) goto exit;
  2915.  
  2916.     InitFieldInfo(wind);
  2917.     err = DoZoom(wind, inZoomOut);
  2918.     if (err != noErr) goto exit;
  2919.  
  2920.     MyShowWindow(wind);
  2921.     
  2922.     if (mailToSelf) {
  2923.         err = SendMessageAndCloseWindow(wind);
  2924.         if (err != noErr) goto exit;
  2925.     }
  2926.     
  2927.     SetPort(port);
  2928.     return noErr;
  2929.     
  2930. exit:
  2931.  
  2932.     MyDisposeHandle(subject);
  2933.     DoClose(wind);
  2934.     SetPort(port);
  2935.     return err;
  2936. }
  2937.  
  2938.  
  2939.  
  2940. /*----------------------------------------------------------------------------
  2941.     BuildRedirectBody 
  2942.     
  2943.     Build the body of a redirect message.
  2944.             
  2945.     Entry:    text = handle to full article text.
  2946.             quoteText = handle to text to be quoted, or nil to quote the
  2947.                 full text.
  2948.             start = offset in quoteText of beginning of text to
  2949.                 be quoted.
  2950.             end = offset in quoteText of end of text to be quoted.
  2951.             body = handle.
  2952.             
  2953.     Exit:    function result = error code.
  2954.             body = handle to message body.
  2955. ----------------------------------------------------------------------------*/
  2956.  
  2957. static OSErr BuildRedirectBody (Handle text, Handle quoteText, long start, 
  2958.     long end, Handle body)
  2959. {
  2960.     long len, headerLen;
  2961.     OSErr err = noErr;
  2962.     
  2963.     headerLen = FindBody(text);
  2964.     
  2965.     if (quoteText == nil) {
  2966.         len = MyGetHandleSize(text) - headerLen;
  2967.     } else {
  2968.         len = end - start;
  2969.     }
  2970.     
  2971.     err = MySetHandleSize(body, len);
  2972.     if (err != noErr) return err;
  2973.     
  2974.     if (quoteText == nil) {
  2975.         BlockMoveData(*text + headerLen, *body, len);
  2976.     } else {
  2977.         BlockMoveData(*quoteText + start, *body, len);
  2978.     }
  2979.     
  2980.     if (len > 0x7fff) {
  2981.         MySetHandleSize(body, 0x7fff);
  2982.         ErrorMessageNumber(kStrTrunc);
  2983.     }
  2984.     
  2985.     return noErr;
  2986. }
  2987.  
  2988.  
  2989.  
  2990. /*----------------------------------------------------------------------------
  2991.     OpenRedirectWindow 
  2992.     
  2993.     Open a redirect window.
  2994.             
  2995.     Entry:    text = handle to full article text.
  2996.             quoteText = handle to text to be quoted, or nil to quote the
  2997.                 full text.
  2998.             start = offset in quoteText of beginning of text to
  2999.                 be quoted.
  3000.             end = offset in quoteText of end of text to be quoted.
  3001.             mailToSelf = true to mail the redirected message to self.
  3002.             
  3003.     Exit:    function result = error code.
  3004. ----------------------------------------------------------------------------*/
  3005.  
  3006. OSErr OpenRedirectWindow (Handle text, Handle quoteText, long start, long end,
  3007.     Boolean mailToSelf)
  3008. {
  3009.     WindowPtr wind = nil;
  3010.     TWindow **info;
  3011.     Handle posterFrom = nil;
  3012.     char myFrom[514];
  3013.     CStr255 byWayOfFmt;
  3014.     char byWayOf[800];
  3015.     OSErr err = noErr;
  3016.     TEHandle theTE;
  3017.     long posterFromLen, byWayOfLen;
  3018.     long headerLen;
  3019.     Handle extraMail = nil;
  3020.     char *p;
  3021.     TEHandle extraMailField;
  3022.     Handle hText;
  3023.     short teLength;
  3024.     GrafPtr port;
  3025.     
  3026.     GetPort(&port);
  3027.     
  3028.     err = MakeNewWindow(&wind);
  3029.     if (err != noErr) goto exit;
  3030.     SetPort(wind);
  3031.     info = (TWindow**)GetWRefCon(wind);
  3032.  
  3033.     (**info).newsIcon = false;
  3034.     if (mailToSelf) {
  3035.         (**info).selfIcon = true;
  3036.     } else {
  3037.         (**info).mailIcon = true;
  3038.     }
  3039.     
  3040.     headerLen = FindBody(text);
  3041.     for (p = *text + headerLen - 1; p >= *text && *p == CR; p--) /* do nothing */;
  3042.     p++;
  3043.     headerLen = p - *text;
  3044.     err = MyNewHandle(headerLen, &extraMail);
  3045.     if (err != noErr) goto exit;
  3046.     BlockMoveData(*text, *extraMail, headerLen);
  3047.     
  3048.     err = FindHeaderHandle(extraMail, "From", &posterFrom);
  3049.     if (err != noErr) goto exit;
  3050.     
  3051.     err = MoveHeaderLine(extraMail, "Subject", (**info).subjectField);
  3052.     if (err != noErr) goto exit;
  3053.     err = MoveHeaderLine(extraMail, "Reply-To", (**info).replytoField);
  3054.     if (err != noErr) goto exit;
  3055.     err = MoveHeaderLine(extraMail, "Keywords", (**info).keywordsField);
  3056.     if (err != noErr) goto exit;
  3057.     DeleteHeaderLine(extraMail, "To");
  3058.     DeleteHeaderLine(extraMail, "Cc");
  3059.     DeleteHeaderLine(extraMail, "Bcc");
  3060.     DeleteHeaderLine(extraMail, "From");
  3061.     
  3062.     ChangeSubject(wind);
  3063.     
  3064.     if (posterFrom != nil) {
  3065.         MakeFromHeader(myFrom);
  3066.         posterFromLen = MyGetHandleSize(posterFrom);
  3067.         GetCString(kStrByWayOfFmt, byWayOfFmt);
  3068.         sprintf(byWayOf, byWayOfFmt, myFrom);
  3069.         byWayOfLen = strlen(byWayOf);
  3070.         err = MySetHandleSize(posterFrom, posterFromLen + byWayOfLen);
  3071.         if (err != noErr) goto exit;
  3072.         BlockMoveData(byWayOf, *posterFrom + posterFromLen, byWayOfLen);
  3073.         (**info).from = posterFrom;
  3074.         posterFrom = nil;
  3075.     }
  3076.     
  3077.     extraMailField = (**info).extraMailField;
  3078.     TESetSelect(0x7fff, 0x7fff, extraMailField);
  3079.     hText = (**extraMailField).hText;
  3080.     teLength = (**extraMailField).teLength;
  3081.     if (teLength > 0 && *(*hText + teLength - 1) != CR) TEKey(CR, extraMailField);
  3082.     err = MyHandAndHand(extraMail, hText);
  3083.     if (err != noErr) goto exit;
  3084.     MyDisposeHandle(extraMail);
  3085.     extraMail = nil;
  3086.     
  3087.     TESetSelect(0, 0x7fff, (**info).signatureField);
  3088.     TEDelete((**info).signatureField);
  3089.     
  3090.     theTE = (**info).theTE;
  3091.     err = BuildRedirectBody(text, quoteText, start, end, (**theTE).hText);
  3092.     if (err != noErr) goto exit;
  3093.  
  3094.     InitFieldInfo(wind);
  3095.     err = DoZoom(wind, inZoomOut);
  3096.     if (err != noErr) goto exit;
  3097.  
  3098.     MyShowWindow(wind);
  3099.     
  3100.     if (mailToSelf) {
  3101.         err = SendMessageAndCloseWindow(wind);
  3102.         if (err != noErr) goto exit;
  3103.     }
  3104.     
  3105.     SetPort(port);
  3106.     return noErr;
  3107.     
  3108. exit:
  3109.  
  3110.     MyDisposeHandle(posterFrom);
  3111.     MyDisposeHandle(extraMail);
  3112.     DoClose(wind);
  3113.     SetPort(port);
  3114.     return err;
  3115. }
  3116.  
  3117.  
  3118.  
  3119. /*----------------------------------------------------------------------------
  3120.     OpenMailWindow 
  3121.     
  3122.     Open a mail message window.
  3123.     
  3124.     Entry:    address = email address of recipient or emailto URL.
  3125.     
  3126.     Exit:    function result = error code.
  3127. ----------------------------------------------------------------------------*/
  3128.  
  3129. OSErr OpenMailWindow (char *address)
  3130. {
  3131.     WindowPtr wind = nil;
  3132.     TWindow **info;
  3133.     CStr255 addr;
  3134.     OSErr err = noErr;
  3135.     GrafPtr port;
  3136.     
  3137.     GetPort(&port);
  3138.     
  3139.     if (MyStrNEqual(address, "mailto::", 8)) {
  3140.         strcpy(addr, address+8);
  3141.     } else if (MyStrNEqual(address, "mailto:", 7)) {
  3142.         strcpy(addr, address+7);
  3143.     } else {
  3144.         strcpy(addr, address);
  3145.     }
  3146.  
  3147.     err = MakeNewWindow(&wind);
  3148.     if (err != noErr) return err;
  3149.     SetPort(wind);
  3150.     info = (TWindow**)GetWRefCon(wind);
  3151.     
  3152.     (**info).newsIcon = false;
  3153.     (**info).mailIcon = true;
  3154.     
  3155.     err = MyTESetText(addr, strlen(addr), (**info).toField);
  3156.     if (err != noErr) goto exit;
  3157.  
  3158.     InitFieldInfo(wind);
  3159.     err = DoZoom(wind, inZoomOut);
  3160.     if (err != noErr) goto exit;
  3161.  
  3162.     MyShowWindow(wind);
  3163.     SetPort(port);
  3164.     return noErr;
  3165.     
  3166. exit:
  3167.  
  3168.     DoClose(wind);
  3169.     SetPort(port);
  3170.     return err;
  3171. }
  3172.  
  3173.  
  3174.  
  3175. /*----------------------------------------------------------------------------
  3176.     ReadMessageWindDataFork 
  3177.     
  3178.     Read the data fork of a saved message window.
  3179.             
  3180.     Entry:    wind = pointer to message window.
  3181.             fSpec = pointer to file spec.
  3182.     
  3183.     Exit:    function result = error code.
  3184.             *truncated = true if body >= 32K and truncated
  3185. ----------------------------------------------------------------------------*/
  3186.  
  3187. static OSErr ReadMessageWindDataFork (WindowPtr wind, FSSpec *fSpec,
  3188.     Boolean *truncated)
  3189. {
  3190.     TWindow **info;
  3191.     OSErr err = noErr;
  3192.     short fRefNum = 0;
  3193.     long length;
  3194.     TEHandle theTE;
  3195.     Handle hText;
  3196.     char state;
  3197.     
  3198.     *truncated = false;
  3199.     
  3200.     info = (TWindow**)GetWRefCon(wind);
  3201.     theTE = (**info).theTE;
  3202.     hText = (**theTE).hText;
  3203.     
  3204.     err = FSpOpenDF(fSpec, fsRdPerm, &fRefNum);
  3205.     if (err != noErr) goto exit;
  3206.     err = GetEOF(fRefNum, &length);
  3207.     if (err != noErr) goto exit;
  3208.     if (length > 0x7fff) {
  3209.         length = 0x7fff;
  3210.         *truncated = true;
  3211.     }
  3212.     
  3213.     err = MySetHandleSize(hText, length);
  3214.     if (err != noErr) goto exit;
  3215.     
  3216.     state = MyHGetState(hText);
  3217.     err = FSRead(fRefNum, &length, *hText);
  3218.     MyHSetState(hText, state);
  3219.     if (err != noErr) goto exit;
  3220.     
  3221.     MyFSClose(fRefNum, nil);
  3222.     return noErr;
  3223.     
  3224. exit:
  3225.  
  3226.     if (fRefNum != 0) MyFSClose(fRefNum, nil);
  3227.     return err;
  3228. }
  3229.  
  3230.  
  3231.  
  3232. /*----------------------------------------------------------------------------
  3233.     ReadOneHandle 
  3234.     
  3235.     Read one relocatable block from a saved message window resource fork.
  3236.             
  3237.     Entry:    id = resource id.
  3238.     
  3239.     Exit:    function result = error code.
  3240.             *theHandle = handle to block, or nil if resource does not exist.
  3241. ----------------------------------------------------------------------------*/
  3242.  
  3243. static OSErr ReadOneHandle (short id, Handle *theHandle)
  3244. {
  3245.     Handle h = nil;
  3246.     OSErr err = noErr;
  3247.     
  3248.     err = MyGet1Resource('TEXT', id, &h);
  3249.     if (err == resNotFound) {
  3250.         *theHandle = nil;
  3251.         return noErr;
  3252.     }
  3253.     if (err != noErr) return err;
  3254.     MyDetachResource(h);
  3255.     *theHandle = h;
  3256.     return noErr;
  3257. }
  3258.  
  3259.  
  3260.  
  3261. /*----------------------------------------------------------------------------
  3262.     ReadOneTEField 
  3263.     
  3264.     Read one saved TE field from a saved message window resource fork.
  3265.             
  3266.     Entry:    field = handle to TE field.
  3267.             id = resource id.
  3268.     
  3269.     Exit:    function result = error code.
  3270.             *truncated = true if field >= 32K and truncated.
  3271. ----------------------------------------------------------------------------*/
  3272.  
  3273. static OSErr ReadOneTEField (TEHandle field, short id, Boolean *truncated)
  3274. {
  3275.     Handle hText;
  3276.     long len;
  3277.     OSErr err = noErr;
  3278.  
  3279.     *truncated = false;
  3280.     err = ReadOneHandle(id, &hText);
  3281.     if (err != noErr) return err;
  3282.     if (hText == nil) return noErr;
  3283.     len = MyGetHandleSize(hText);
  3284.     if (len > 0x7fff) {
  3285.         len = 0x7fff;
  3286.         *truncated = true;
  3287.     }
  3288.     MyHLock(hText);
  3289.     err = MyTESetText(*hText, len, field);
  3290.     MyDisposeHandle(hText);
  3291.     return err;
  3292. }
  3293.  
  3294.  
  3295.  
  3296. /*----------------------------------------------------------------------------
  3297.     ReadMessageWindResourceFork 
  3298.     
  3299.     Read the resource fork of a saved message window.
  3300.             
  3301.     Entry:    wind = pointer to message window.
  3302.             fSpec = pointer to file spec.
  3303.     
  3304.     Exit:    function result = error code.
  3305.             *truncated = true if field >= 32K and truncated.
  3306.             *pos = saved window position.
  3307. ----------------------------------------------------------------------------*/
  3308.  
  3309. static OSErr ReadMessageWindResourceFork (WindowPtr wind, FSSpec *fSpec,
  3310.     Boolean *truncated, TSavedWindPos *pos)
  3311. {
  3312.     TWindow **info;
  3313.     OSErr err = noErr;
  3314.     short refNum = 0;
  3315.     TMiscMessageWindowInfo **miscInfo = nil;
  3316.     CStr255 tabStopsValueStr;
  3317.     Handle theHandle, h;
  3318.     Boolean t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13;
  3319.     
  3320.     *truncated = false;
  3321.     pos->valid = false;
  3322.     
  3323.     info = (TWindow**)GetWRefCon(wind);
  3324.     
  3325.     err = MyFSpOpenResFile(fSpec, fsRdPerm, &refNum);
  3326.     if (err == eofErr || err == fnfErr) return noErr;
  3327.     if (err != noErr) goto exit;
  3328.     
  3329.     err = MyGet1Resource('MISC', 128, &miscInfo);
  3330.     if (err == noErr) {
  3331.         (**info).newsIcon = (**miscInfo).newsIcon;
  3332.         (**info).mailIcon = (**miscInfo).mailIcon;
  3333.         (**info).selfIcon = (**miscInfo).selfIcon;
  3334.         (**info).tabEnabled = (**miscInfo).tabEnabled;
  3335.         (**info).wrapOnSend = (**miscInfo).wrapOnSend;
  3336.         (**info).tabStops = (**miscInfo).tabStops;
  3337.         MyReleaseResource(miscInfo);
  3338.     }
  3339.     
  3340.     SetControlValue((**info).tabCheckbox, (**info).tabEnabled ? 1 : 0);
  3341.     SetControlValue((**info).wrapCheckbox, (**info).wrapOnSend ? 1 : 0);
  3342.     sprintf(tabStopsValueStr, "%d", (**info).tabStops);
  3343.     err = MyTESetText(tabStopsValueStr, strlen(tabStopsValueStr), (**info).tabField);
  3344.     if (err != noErr) goto exit;
  3345.     
  3346.     err = ReadOneTEField((**info).quoteStringField, kQuoteStringResID, &t1);
  3347.     if (err != noErr) goto exit;
  3348.     err = ReadOneTEField((**info).newsgroupsField, kNewsgroupsResID, &t2);
  3349.     if (err != noErr) goto exit;
  3350.     err = ReadOneTEField((**info).toField, kToResID, &t3);
  3351.     if (err != noErr) goto exit;
  3352.     err = ReadOneTEField((**info).subjectField, kSubjectResID, &t4);
  3353.     if (err != noErr) goto exit;
  3354.     err = ReadOneTEField((**info).ccField, kCcResID, &t5);
  3355.     if (err != noErr) goto exit;
  3356.     err = ReadOneTEField((**info).bccField, kBccResID, &t6);
  3357.     if (err != noErr) goto exit;
  3358.     err = ReadOneTEField((**info).replytoField, kReplytoResID, &t7);
  3359.     if (err != noErr) goto exit;
  3360.     err = ReadOneTEField((**info).followuptoField, kFollowuptoResID, &t8);
  3361.     if (err != noErr) goto exit;
  3362.     err = ReadOneTEField((**info).keywordsField, kKeywordsResID, &t9);
  3363.     if (err != noErr) goto exit;
  3364.     err = ReadOneTEField((**info).distributionField, kDistributionResID, &t10);
  3365.     if (err != noErr) goto exit;
  3366.     err = ReadOneTEField((**info).extraNewsField, kExtraNewsResID, &t11);
  3367.     if (err != noErr) goto exit;
  3368.     err = ReadOneTEField((**info).extraMailField, kExtraMailResID, &t12);
  3369.     if (err != noErr) goto exit;
  3370.     err = ReadOneTEField((**info).signatureField, kSignatureResID, &t13);
  3371.     if (err != noErr) goto exit;
  3372.     
  3373.     *truncated = t1 || t2 || t3 || t4 || t5 || t6 || t7 || t8 ||
  3374.         t9 || t10 || t11 || t12 || t13;
  3375.     
  3376.     err = ReadOneHandle(kReferencesResID, &theHandle);
  3377.     if (err != noErr) goto exit;
  3378.     (**info).references = theHandle;
  3379.     
  3380.     err = ReadOneHandle(kFromResID, &theHandle);
  3381.     if (err != noErr) goto exit;
  3382.     (**info).from = theHandle;
  3383.     
  3384.     err = MyGet1Resource('WPOS', 128, &h);
  3385.     if (err == noErr) {
  3386.         if (MyGetHandleSize(h) == sizeof(Point)) {
  3387.             pos->valid = true;
  3388.             pos->oldFormat = true;
  3389.             BlockMoveData(*h, &pos->userState, sizeof(Point));
  3390.         } else {
  3391.             BlockMoveData(*h, pos, sizeof(TSavedWindPos));
  3392.         }
  3393.     }
  3394.     
  3395.     MyCloseResFile(refNum);
  3396.     return noErr;
  3397.     
  3398. exit:
  3399.  
  3400.     if (refNum != 0) MyCloseResFile(refNum);
  3401.     return err;
  3402. }
  3403.  
  3404.  
  3405.  
  3406. /*----------------------------------------------------------------------------
  3407.     OpenMessageFile 
  3408.     
  3409.     Open a saved message.
  3410.             
  3411.     Entry:    fSpec = pointer to file spec.
  3412.     
  3413.     Exit:    function result = error code.
  3414.             *wind = pointer to opened message window.
  3415. ----------------------------------------------------------------------------*/
  3416.  
  3417. OSErr OpenMessageFile (FSSpec *fSpec, WindowPtr *wind)
  3418. {
  3419.     OSErr err = noErr;
  3420.     WindowPtr w = nil;
  3421.     TWindow** info;
  3422.     FSSpec windFile;
  3423.     GrafPtr port;
  3424.     Boolean bodyTruncated, fieldTruncated;
  3425.     Boolean wasChanged;
  3426.     AliasHandle alias;
  3427.     TWindowKind kind;
  3428.     TSavedWindPos pos;
  3429.     Boolean needsZooming;
  3430.     
  3431.     GetPort(&port);
  3432.  
  3433.     /* Check to see if the file is already open. If it is, bring its
  3434.        window to the front. */
  3435.     
  3436.     for (w = FrontWindow(); 
  3437.         w != nil; 
  3438.         w = (WindowPtr)((WindowPeek)w)->nextWindow) 
  3439.     {
  3440.         kind = GetMyWindowKind(w);
  3441.         if (kind == kMessage) {
  3442.             info = (TWindow**)GetWRefCon(w);
  3443.             if ((**info).alias != nil) {
  3444.                 err = ResolveAlias(nil, (**info).alias, &windFile, &wasChanged);
  3445.                 if (err == noErr && IsEqualFSSpec(&windFile, fSpec)) {
  3446.                     MySelectWindow(w);
  3447.                     *wind = w;
  3448.                     return noErr;
  3449.                 }
  3450.             }
  3451.         }
  3452.     }
  3453.     
  3454.     /* Make a new message window and read it from the file. */
  3455.     
  3456.     err = MakeNewWindow(&w);
  3457.     if (err != noErr) goto exit;
  3458.     SetPort(w);
  3459.     
  3460.     err = ReadMessageWindDataFork(w, fSpec, &bodyTruncated);
  3461.     if (err != noErr) goto exit;
  3462.     
  3463.     err = ReadMessageWindResourceFork(w, fSpec, &fieldTruncated, &pos);
  3464.     if (err != noErr) goto exit;
  3465.     
  3466.     info = (TWindow**)GetWRefCon(w);
  3467.     (**info).changed = false;
  3468.     
  3469.     SetWTitle(w, fSpec->name);
  3470.     
  3471.     err = NewAlias(nil, fSpec, &alias);
  3472.     if (err != noErr) goto exit;
  3473.     (**info).alias = alias;
  3474.  
  3475.     InitFieldInfo(w);
  3476.     
  3477.     RestoreWindPos(w, &pos, &needsZooming);
  3478.     ResizeContents(w);
  3479.     if (needsZooming) {
  3480.         err = DoZoom(w, inZoomOut);
  3481.         if (err != noErr) goto exit;
  3482.     }
  3483.     (**info).movedSinceLastSave = false;
  3484.  
  3485.     MyShowWindow(w);
  3486.  
  3487.     SetPort(port);
  3488.     
  3489.     if (bodyTruncated) CautionMessageNumber(kStrMessageBodyTruncated);
  3490.     if (fieldTruncated) CautionMessageNumber(kStrMessageFieldTruncated);
  3491.     
  3492.     *wind = w;
  3493.     return noErr;
  3494.     
  3495. exit:
  3496.  
  3497.     DoClose(w);
  3498.     SetPort(port);
  3499.     return err;
  3500. }
  3501.  
  3502.  
  3503.  
  3504. /*----------------------------------------------------------------------------
  3505.     FormFileName 
  3506.     
  3507.     Form the default file name for saving a message.
  3508.             
  3509.     Entry:    wind = pointer to message window.
  3510.     
  3511.     Exit:    fileName = default file name.
  3512. ----------------------------------------------------------------------------*/
  3513.  
  3514. static void FormFileName (WindowPtr wind, Str31 fileName)
  3515. {
  3516.     Str255 title;
  3517.     
  3518.     ChangeSubject(wind);
  3519.     GetWTitle(wind, title);
  3520.     MakeLegalFileName(title, fileName);
  3521. }
  3522.  
  3523.  
  3524.  
  3525. /*----------------------------------------------------------------------------
  3526.     PresentStandardMessageSaveFileDialog 
  3527.     
  3528.     Present the standard save file dialog.
  3529.             
  3530.     Entry:    fileName = default file name.
  3531.     
  3532.     Exit:    function result = error code.
  3533.             *fSpec = file spec.
  3534.             *scriptTag = script code.
  3535. ----------------------------------------------------------------------------*/
  3536.  
  3537. static OSErr PresentStandardMessageSaveFileDialog (Str31 fileName, FSSpec *fSpec,
  3538.     ScriptCode *scriptTag)
  3539. {
  3540.     StandardFileReply reply;
  3541.     Str255 prompt;
  3542.     
  3543.     GetPString(kStrSaveMessagePrompt, prompt);
  3544.     MyStandardPutFile(prompt, fileName, &reply, 
  3545.         gPrefs.savedMsgDefaultFolder ? gPrefs.savedMsgDefaultFolderAlias : nil);
  3546.     if (!reply.sfGood) return userCanceledErr;
  3547.     *fSpec = reply.sfFile;
  3548.     *scriptTag = reply.sfScript;
  3549.     return noErr;
  3550. }
  3551.  
  3552.  
  3553.  
  3554. /*----------------------------------------------------------------------------
  3555.     CheckMessageFileExists 
  3556.     
  3557.     Check to see if a message file already exists in the default message
  3558.     file save directory. If it does, present an alert asking the user to
  3559.     replace, cancel, or pick a new name.
  3560.             
  3561.     Entry:    fileName = file name.
  3562.     
  3563.     Exit:    function result = error code.
  3564.             *fSpec = file spec.
  3565.             *scriptTag = script code.
  3566. ----------------------------------------------------------------------------*/
  3567.  
  3568. static OSErr CheckMessageFileExists (Str31 fileName, FSSpec *fSpec, ScriptCode *scriptTag)
  3569. {
  3570.     OSErr err = noErr;
  3571.     DialogPtr dlg = nil;
  3572.     Boolean valid;
  3573.     short item;
  3574.  
  3575.     ValidateSavedFolderAlias(gPrefs.savedMsgDefaultFolderAlias, &fSpec->vRefNum, 
  3576.         &fSpec->parID, &valid);
  3577.     if (!valid) {
  3578.         ErrorMessageNumber(kStrDefaultMsgFoldNotFound);
  3579.         err = PresentStandardMessageSaveFileDialog(fileName, fSpec, scriptTag);
  3580.         if (err != noErr) return err;
  3581.         return noErr;
  3582.     }
  3583.     *scriptTag = smSystemScript;
  3584.     CopyPascalString(fSpec->name, fileName);
  3585.     err = FileOrFolderExists(fSpec);
  3586.     if (err == fnfErr) return noErr;
  3587.     if (err != noErr) return err;
  3588.     
  3589.     err = MyGetNewDialog(kMessageFileExistsAlert, 1, 2, &dlg);
  3590.     if (err != noErr) return err;
  3591.     ParamText(fileName, "\p", "\p", "\p");
  3592.     SetItemKeyEquivalent(dlg, 3, 'R');
  3593.     SysBeep(0);
  3594.     MyModalDialog(dlg, gDialogFilterUPP, &item);
  3595.     err = DoClose(dlg);
  3596.     if (err != noErr) return err;
  3597.     switch (item) {
  3598.         case 1: /* Pick a New Name */
  3599.             err = PresentStandardMessageSaveFileDialog(fileName, fSpec, scriptTag);
  3600.             if (err != noErr) return err;
  3601.             return noErr;
  3602.         case 2: /* Cancel */
  3603.             return userCanceledErr;
  3604.         case 3: /* Replace */
  3605.             return noErr;
  3606.     }
  3607.     return noErr;
  3608. }
  3609.  
  3610.  
  3611.  
  3612. /*----------------------------------------------------------------------------
  3613.     SaveMessageWindDataFork 
  3614.     
  3615.     Save a message window data fork.
  3616.             
  3617.     Entry:    wind = pointer to message window.
  3618.             fSpec = pointer to file spec.
  3619.             scriptTag = script code.
  3620.     
  3621.     Exit:    function result = error code.
  3622. ----------------------------------------------------------------------------*/
  3623.  
  3624. static OSErr SaveMessageWindDataFork (WindowPtr wind, FSSpec *fSpec, ScriptCode scriptTag)
  3625. {
  3626.     TWindow **info;
  3627.     TEHandle theTE;
  3628.     Handle text;
  3629.     OSErr err = noErr;
  3630.     long length;
  3631.     short refNum = 0;
  3632.     char state;
  3633.     Boolean empty;
  3634.  
  3635.     info = (TWindow**)GetWRefCon(wind);
  3636.     theTE = (**info).theTE;
  3637.     text = (**theTE).hText;
  3638.  
  3639.     state = MyHGetState(text);
  3640.     
  3641.     err = OpenDataForkWriteCreateIfMissing(fSpec, kNewsWatcherSignature, 
  3642.         kSavedMessageFileType, scriptTag, false, &refNum, &empty);
  3643.     if (err != noErr) goto exit;
  3644.  
  3645.     length = MyGetHandleSize(text);
  3646.     MyHLock(text);
  3647.     err = MyFSWriteNoCache(refNum, &length, *text, nil);
  3648.     if (err != noErr) goto exit;
  3649.     MyHSetState(text, state);
  3650.     MyFSClose(refNum, nil);
  3651.     return noErr;
  3652.     
  3653. exit:
  3654.  
  3655.     MyHSetState(text, state);
  3656.     if (refNum != 0) MyFSClose(refNum, nil);
  3657.     return err;
  3658. }
  3659.  
  3660.  
  3661.  
  3662. /*----------------------------------------------------------------------------
  3663.     SaveOneHandle 
  3664.     
  3665.     Save one relocatable block on a message window resource fork.
  3666.             
  3667.     Entry:    theHandle = handle to block to be saved.
  3668.             id = resource id.
  3669.     
  3670.     Exit:    function result = error code.
  3671. ----------------------------------------------------------------------------*/
  3672.  
  3673. static OSErr SaveOneHandle (Handle theHandle, short id)
  3674. {
  3675.     OSErr err = noErr;
  3676.     
  3677.     if (theHandle == nil) return noErr;
  3678.     err = MyReplaceResource(theHandle, 'TEXT', id, "\p");
  3679.     if (err != noErr) return err;
  3680.     WriteResource(theHandle);
  3681.     DetachResource(theHandle);
  3682.     return noErr;
  3683. }
  3684.  
  3685.  
  3686.  
  3687. /*----------------------------------------------------------------------------
  3688.     SaveOneTEField 
  3689.     
  3690.     Save one TE field on a message window resource fork.
  3691.             
  3692.     Entry:    field = handle to TE field.
  3693.             id = resource id.
  3694.     
  3695.     Exit:    function result = error code.
  3696. ----------------------------------------------------------------------------*/
  3697.  
  3698. static OSErr SaveOneTEField (TEHandle field, short id)
  3699. {
  3700.     return SaveOneHandle((**field).hText, id);
  3701. }
  3702.  
  3703.  
  3704.  
  3705. /*----------------------------------------------------------------------------
  3706.     SaveMessageWindResourceFork 
  3707.     
  3708.     Save a message window resource fork.
  3709.             
  3710.     Entry:    wind = pointer to message window.
  3711.             fSpec = pointer to file spec.
  3712.             scriptTag = script code.
  3713.     
  3714.     Exit:    function result = error code.
  3715. ----------------------------------------------------------------------------*/
  3716.  
  3717. static OSErr SaveMessageWindResourceFork (WindowPtr wind, FSSpec *fSpec, 
  3718.     ScriptCode scriptTag)
  3719. {
  3720.     TWindow **info;
  3721.     short refNum = 0;
  3722.     OSErr err = noErr;
  3723.     TMiscMessageWindowInfo **miscInfo = nil;
  3724.     
  3725.     info = (TWindow**)GetWRefCon(wind);
  3726.     
  3727.     err = OpenResFileWriteCreateIfMissing(fSpec, kNewsWatcherSignature, 
  3728.         kSavedMessageFileType, scriptTag, &refNum);
  3729.     if (err != noErr) goto exit;
  3730.     
  3731.     err = MyNewHandle(sizeof(TMiscMessageWindowInfo), &miscInfo);
  3732.     if (err != noErr) goto exit;
  3733.     (**miscInfo).newsIcon = (**info).newsIcon;
  3734.     (**miscInfo).mailIcon = (**info).mailIcon;
  3735.     (**miscInfo).selfIcon = (**info).selfIcon;
  3736.     (**miscInfo).tabEnabled = (**info).tabEnabled;
  3737.     (**miscInfo).wrapOnSend = (**info).wrapOnSend;
  3738.     (**miscInfo).tabStops = (**info).tabStops;
  3739.     err = MyReplaceResource(miscInfo, 'MISC', 128, "\p");
  3740.     if (err != noErr) goto exit;
  3741.     miscInfo = nil;
  3742.     
  3743.     err = SaveOneTEField((**info).quoteStringField, kQuoteStringResID);
  3744.     if (err != noErr) goto exit;
  3745.     err = SaveOneTEField((**info).newsgroupsField, kNewsgroupsResID);
  3746.     if (err != noErr) goto exit;
  3747.     err = SaveOneTEField((**info).toField, kToResID);
  3748.     if (err != noErr) goto exit;
  3749.     err = SaveOneTEField((**info).subjectField, kSubjectResID);
  3750.     if (err != noErr) goto exit;
  3751.     err = SaveOneTEField((**info).ccField, kCcResID);
  3752.     if (err != noErr) goto exit;
  3753.     err = SaveOneTEField((**info).bccField, kBccResID);
  3754.     if (err != noErr) goto exit;
  3755.     err = SaveOneTEField((**info).replytoField, kReplytoResID);
  3756.     if (err != noErr) goto exit;
  3757.     err = SaveOneTEField((**info).followuptoField, kFollowuptoResID);
  3758.     if (err != noErr) goto exit;
  3759.     err = SaveOneTEField((**info).keywordsField, kKeywordsResID);
  3760.     if (err != noErr) goto exit;
  3761.     err = SaveOneTEField((**info).distributionField, kDistributionResID);
  3762.     if (err != noErr) goto exit;
  3763.     err = SaveOneTEField((**info).extraNewsField, kExtraNewsResID);
  3764.     if (err != noErr) goto exit;
  3765.     err = SaveOneTEField((**info).extraMailField, kExtraMailResID);
  3766.     if (err != noErr) goto exit;
  3767.     err = SaveOneTEField((**info).signatureField, kSignatureResID);
  3768.     if (err != noErr) goto exit;
  3769.     
  3770.     err = SaveOneHandle((**info).references, kReferencesResID);
  3771.     if (err != noErr) goto exit;
  3772.     err = SaveOneHandle((**info).from, kFromResID);
  3773.     if (err != noErr) goto exit;
  3774.     
  3775.     err = WriteProgramNameResource(kStrNewsWatcher);
  3776.     if (err != noErr) goto exit;
  3777.     
  3778.     err = SaveWindPosAsResource(wind);
  3779.     if (err != noErr) goto exit;
  3780.     
  3781.     MyCloseResFile(refNum);
  3782.     
  3783.     return noErr;
  3784.     
  3785. exit:
  3786.  
  3787.     if (refNum != 0) MyCloseResFile(refNum);
  3788.     MyDisposeHandle(miscInfo);
  3789.     return err;
  3790. }
  3791.  
  3792.  
  3793.  
  3794. /*----------------------------------------------------------------------------
  3795.     SaveMessageWindToFile 
  3796.     
  3797.     Save a message window to a file.
  3798.             
  3799.     Entry:    wind = pointer to message window.
  3800.             fSpec = pointer to file spec.
  3801.             scriptTag = script code.
  3802.     
  3803.     Exit:    function result = error code.
  3804. ----------------------------------------------------------------------------*/
  3805.  
  3806. static OSErr SaveMessageWindToFile (WindowPtr wind, FSSpec *fSpec, ScriptCode scriptTag)
  3807. {
  3808.     TWindow **info;
  3809.     OSErr err = noErr;
  3810.     Boolean savedCriticalSeq;
  3811.     AliasHandle alias;
  3812.     
  3813.     BeginCriticalMemorySequence(&savedCriticalSeq);
  3814.  
  3815.     info = (TWindow**)GetWRefCon(wind);
  3816.     
  3817.     err = SaveMessageWindDataFork(wind, fSpec, scriptTag);
  3818.     if (err != noErr) goto exit;
  3819.     
  3820.     err = SaveMessageWindResourceFork(wind, fSpec, scriptTag);
  3821.     if (err != noErr) goto exit;
  3822.     
  3823.     err = NewAlias(nil, fSpec, &alias);
  3824.     if (err != noErr) goto exit;
  3825.     MyDisposeHandle((**info).alias);
  3826.     (**info).alias = alias;
  3827.     (**info).changed = false;
  3828.     
  3829.     EndCriticalMemorySequence(savedCriticalSeq);
  3830.     
  3831.     return noErr;
  3832.     
  3833. exit:
  3834.  
  3835.     EndCriticalMemorySequence(savedCriticalSeq);
  3836.     return err;
  3837. }
  3838.  
  3839.  
  3840.  
  3841. /*----------------------------------------------------------------------------
  3842.     MakeNewTestMessageWindow 
  3843.     
  3844.     Create a new message window for testing (development version only).
  3845.     
  3846.     Exit:    function result = error code.
  3847. ----------------------------------------------------------------------------*/
  3848.  
  3849. #ifdef kDevelopmentVersion
  3850.  
  3851. OSErr MakeNewTestMessageWindow (void)
  3852. {
  3853.     WindowPtr wind;
  3854.     OSErr err = noErr;
  3855.     GrafPtr port;
  3856.     
  3857.     GetPort(&port);
  3858.  
  3859.     err = MakeNewWindow(&wind);
  3860.     if (err != noErr) return err;
  3861.     SetPort(wind);
  3862.     InitFieldInfo(wind);
  3863.     err = DoZoom(wind, inZoomOut);
  3864.     if (err != noErr) goto exit;
  3865.     MyShowWindow(wind);
  3866.     SetPort(port);
  3867.     return noErr;
  3868.     
  3869. exit:
  3870.  
  3871.     DoClose(wind);
  3872.     SetPort(port);
  3873.     return err;
  3874. }
  3875.  
  3876. #endif
  3877.  
  3878.  
  3879.  
  3880. /*----------------------------------------------------------------------------
  3881.     Find 
  3882.     
  3883.     Search a message window for a pattern.
  3884.             
  3885.     Entry:    wind = pointer to text window.
  3886.             offset = offset into text to begin search.
  3887.             gFindPattern = pattern.
  3888.     
  3889.     Exit:    function result = error code.
  3890. ----------------------------------------------------------------------------*/
  3891.  
  3892. static OSErr Find (WindowPtr wind, short offset)
  3893. {
  3894.     TWindow **info;
  3895.     short curField;
  3896.     TMsgFieldInfo **fields;
  3897.     TEHandle edit;
  3898.     Handle hText;
  3899.     short len;
  3900.     char state;
  3901.     long matchOffset;
  3902.     OSErr err = noErr;
  3903.     
  3904.     info = (TWindow**)GetWRefCon(wind);
  3905.     curField = (**info).curField;
  3906.     fields = (**info).fields;
  3907.     edit = (*fields)[curField].edit;
  3908.     hText = (**edit).hText;
  3909.     len = (**edit).teLength - offset;
  3910.     state = MyHGetState(hText);
  3911.     MyHLock(hText);
  3912.     err = MyNSubstringSearch(*hText+offset, gFindPattern, len, &matchOffset, 
  3913.         GiveTime);
  3914.     MyHSetState(hText, state);
  3915.     if (err != noErr) return err;
  3916.     if (matchOffset == -1) {
  3917.         SysBeep(0);
  3918.     } else {
  3919.         offset += matchOffset;
  3920.         TESetSelect(offset, offset + strlen(gFindPattern), edit);
  3921.         ScrollToMiddle(wind, offset);
  3922.     }
  3923.     return noErr;
  3924. }
  3925.  
  3926.  
  3927.  
  3928. /*----------------------------------------------------------------------------
  3929.     DoNewMessage 
  3930.     
  3931.     Handle the "New Message" command.
  3932.     
  3933.     Exit:    function result = error code.
  3934. ----------------------------------------------------------------------------*/
  3935.  
  3936. OSErr DoNewMessage (void)
  3937. {
  3938.     WindowPtr wind;
  3939.     TWindow **info;
  3940.     OSErr err = noErr;
  3941.     GrafPtr port;
  3942.     
  3943.     GetPort(&port);
  3944.  
  3945.     err = MakeNewWindow(&wind);
  3946.     if (err != noErr) return err;
  3947.     SetPort(wind);
  3948.     info = (TWindow**)GetWRefCon(wind);
  3949.     
  3950.     (**info).newsIcon = true;
  3951.     (**info).mailIcon = false;
  3952.  
  3953.     err = InitializeGroupList(MyFrontWindow(), (**info).newsgroupsField);
  3954.     if (err != noErr) goto exit;
  3955.  
  3956.     InitFieldInfo(wind);
  3957.     err = DoZoom(wind, inZoomOut);
  3958.     if (err != noErr) goto exit;
  3959.  
  3960.     MyShowWindow(wind);
  3961.     SetPort(port);
  3962.     return noErr;
  3963.     
  3964. exit:
  3965.  
  3966.     DoClose(wind);
  3967.     SetPort(port);
  3968.     return err;
  3969. }
  3970.  
  3971.  
  3972.  
  3973. /*----------------------------------------------------------------------------
  3974.     DoSave 
  3975.     
  3976.     Handle the "Save" command.
  3977.             
  3978.     Entry:    wind = pointer to message window.
  3979.             modifiers = modifiers field from event record.
  3980.     
  3981.     Exit:    function result = error code.
  3982. ----------------------------------------------------------------------------*/
  3983.  
  3984. static OSErr DoSave (WindowPtr wind, short modifiers)
  3985. {
  3986.     TWindow **info;
  3987.     Str31 fileName;
  3988.     FSSpec fSpec;
  3989.     ScriptCode scriptTag;
  3990.     OSErr err = noErr;
  3991.     AliasHandle alias;
  3992.     Boolean wasChanged;
  3993.  
  3994.     info = (TWindow**)GetWRefCon(wind);
  3995.     alias = (**info).alias;
  3996.     if (alias != nil) {
  3997.         err = ResolveAlias(nil, alias, &fSpec, &wasChanged);
  3998.         if (err != noErr) alias = nil;
  3999.         scriptTag = smSystemScript;
  4000.     }
  4001.     if (alias == nil) {
  4002.         FormFileName(wind, fileName);
  4003.         if (gPrefs.savedMsgDefaultFolder && (modifiers & optionKey) == 0) {
  4004.             err = CheckMessageFileExists(fileName, &fSpec, &scriptTag);
  4005.             if (err != noErr) return err;
  4006.         } else {
  4007.             err = PresentStandardMessageSaveFileDialog(fileName, &fSpec, &scriptTag);
  4008.             if (err != noErr) return err;
  4009.         }
  4010.     }
  4011.     return SaveMessageWindToFile(wind, &fSpec, scriptTag);
  4012. }
  4013.  
  4014.  
  4015.  
  4016. /*----------------------------------------------------------------------------
  4017.     DoSaveAs
  4018.     
  4019.     Handle the "Save As" command.
  4020.             
  4021.     Entry:    wind = pointer to message window.
  4022.     
  4023.     Exit:    function result = error code.
  4024. ----------------------------------------------------------------------------*/
  4025.  
  4026. static OSErr DoSaveAs (WindowPtr wind)
  4027. {
  4028.     Str31 fileName;
  4029.     FSSpec fSpec;
  4030.     ScriptCode scriptTag;
  4031.     OSErr err = noErr;
  4032.  
  4033.     FormFileName(wind, fileName);
  4034.     err = PresentStandardMessageSaveFileDialog(fileName, &fSpec, &scriptTag);
  4035.     if (err != noErr) return err;
  4036.     return SaveMessageWindToFile(wind, &fSpec, scriptTag);
  4037. }
  4038.  
  4039.  
  4040.  
  4041. /*----------------------------------------------------------------------------
  4042.     DoPrint 
  4043.     
  4044.     Handle the "Print" command.
  4045.             
  4046.     Entry:    wind = pointer to article window.
  4047.             modifiers = modifiers field from event record.
  4048.             
  4049.     Exit:    function result = error code.
  4050. ----------------------------------------------------------------------------*/
  4051.  
  4052. static OSErr DoPrint (WindowPtr wind, short modifiers)
  4053. {
  4054.     TWindow **info;
  4055.     TEHandle theTE;
  4056.     Handle text;
  4057.     OSErr err = noErr;
  4058.     CStr255 title;
  4059.     long start, end;
  4060.  
  4061.     err = StartPrint();
  4062.     if (err != noErr) return err;
  4063.     
  4064.     err = DisplayStatusMessageNumber(kStrPrinting);
  4065.     if (err != noErr) return err;
  4066.  
  4067.     GetWTitle(wind, (StringPtr)title);
  4068.     p2cstr((StringPtr)title);
  4069.     
  4070.     info = (TWindow**)GetWRefCon(wind);
  4071.     theTE = (**info).theTE;
  4072.     start = (**theTE).selStart;
  4073.     end = (**theTE).selEnd;
  4074.     
  4075.     if ((modifiers & shiftKey) == 0 || start >= end) {
  4076.         err = BuildMessageForPrinting(wind, &text);
  4077.         if (err != noErr) return err;
  4078.         start = 0;
  4079.         end = MyGetHandleSize(text);
  4080.         err = PrintText(text, start, end, title);
  4081.         MyDisposeHandle(text);
  4082.         return err;
  4083.     } else {
  4084.         text = (**theTE).hText;
  4085.         return PrintText(text, start, end, title);
  4086.     }
  4087.     
  4088.     if ((modifiers & shiftKey) == 0 || start >= end) {
  4089.         start = 0;
  4090.         end = MyGetHandleSize(text);
  4091.     }
  4092. }
  4093.  
  4094.  
  4095.  
  4096. /*----------------------------------------------------------------------------
  4097.     DoCut 
  4098.     
  4099.     Handle the "Cut" command.
  4100.             
  4101.     Entry:    wind = pointer to active message window.
  4102. ----------------------------------------------------------------------------*/
  4103.  
  4104. static void DoCut (WindowPtr wind)
  4105. {
  4106.     TWindow **info;
  4107.     short curField;
  4108.     TMsgFieldInfo **fields;
  4109.     TEHandle edit;
  4110.     
  4111.     info = (TWindow**)GetWRefCon(wind);
  4112.     curField = (**info).curField;
  4113.     fields = (**info).fields;
  4114.     edit = (*fields)[curField].edit;
  4115.     MyTECut(edit);
  4116.     AdjustCurFieldHeight(wind);
  4117.     (**info).changed = true;
  4118. }
  4119.  
  4120.  
  4121.  
  4122. /*----------------------------------------------------------------------------
  4123.     DoCopy 
  4124.     
  4125.     Handle the "Copy" command.
  4126.             
  4127.     Entry:    wind = pointer to active message window.
  4128. ----------------------------------------------------------------------------*/
  4129.  
  4130. static void DoCopy (WindowPtr wind)
  4131. {
  4132.     TWindow **info;
  4133.     short curField;
  4134.     TMsgFieldInfo **fields;
  4135.     TEHandle edit;
  4136.     
  4137.     info = (TWindow**)GetWRefCon(wind);
  4138.     curField = (**info).curField;
  4139.     fields = (**info).fields;
  4140.     edit = (*fields)[curField].edit;
  4141.     MyTECopy(edit);
  4142. }
  4143.  
  4144.  
  4145.  
  4146. /*----------------------------------------------------------------------------
  4147.     DoPaste 
  4148.     
  4149.     Handle the "Paste" and "Paste as Quotation" commands.
  4150.             
  4151.     Entry:    wind = pointer to active message window.
  4152.             quote = true if "Paste as Quotation".
  4153.             
  4154.     Exit:    function result = error code.
  4155. ----------------------------------------------------------------------------*/
  4156.  
  4157. static OSErr DoPaste (WindowPtr wind, Boolean quote)
  4158. {
  4159.     TWindow **info;
  4160.     short curField;
  4161.     TMsgFieldInfo **fields;
  4162.     TEHandle edit;
  4163.     long scrapOffset;
  4164.     OSErr err = noErr;
  4165.     
  4166.     gDragData = nil;
  4167.     
  4168.     info = (TWindow**)GetWRefCon(wind);
  4169.     curField = (**info).curField;
  4170.     if (curField < (**info).firstScrollingField && quote) return noErr;
  4171.     fields = (**info).fields;
  4172.     edit = (*fields)[curField].edit;
  4173.     
  4174.     if (!quote && GetScrap(nil, kNewsWatcherSignature, &scrapOffset) > 0 &&
  4175.         (edit == (**info).newsgroupsField || edit == (**info).followuptoField))
  4176.     {
  4177.     
  4178.         err = MyNewHandle(0, &gDragData);
  4179.         if (err != noErr) goto exit;
  4180.         GetScrap(gDragData, kNewsWatcherSignature, &scrapOffset);
  4181.         gDragDestWindow = wind;
  4182.         gFinalDestField = edit;
  4183.         err = HandleGroupDragReceivePostProcessor();
  4184.         if (err != noErr) goto exit;
  4185.         MyDisposeHandle(gDragData);
  4186.         gDragData = nil;
  4187.         
  4188.     } else {
  4189.     
  4190.         err = InsertText(wind, TEScrapHandle(), MyTEGetScrapLen(), edit, 
  4191.             (**edit).selStart, true, quote, true);
  4192.         if (err != noErr) goto exit;
  4193.     }
  4194.     
  4195.     return noErr;
  4196.     
  4197. exit:
  4198.  
  4199.     MyDisposeHandle(gDragData);
  4200.     gDragData = nil;
  4201.     return err;
  4202. }
  4203.  
  4204.  
  4205.  
  4206. /*----------------------------------------------------------------------------
  4207.     DoClear 
  4208.     
  4209.     Handle the "Clear" command.
  4210.             
  4211.     Entry:    wind = pointer to active message window.
  4212. ----------------------------------------------------------------------------*/
  4213.  
  4214. static void DoClear (WindowPtr wind)
  4215. {
  4216.     TWindow **info;
  4217.     short curField;
  4218.     TMsgFieldInfo **fields;
  4219.     TEHandle edit;
  4220.     Boolean extraSpaceDeleted;
  4221.     
  4222.     info = (TWindow**)GetWRefCon(wind);
  4223.     curField = (**info).curField;
  4224.     fields = (**info).fields;
  4225.     edit = (*fields)[curField].edit;
  4226.     MyTEDelete(edit, false, &extraSpaceDeleted);
  4227.     AdjustCurFieldHeight(wind);
  4228.     (**info).changed = true;
  4229. }
  4230.  
  4231.  
  4232.  
  4233. /*----------------------------------------------------------------------------
  4234.     DoSelectAll 
  4235.     
  4236.     Handle the "Select All" command.
  4237.             
  4238.     Entry:    wind = pointer to active message window.
  4239. ----------------------------------------------------------------------------*/
  4240.  
  4241. static void DoSelectAll (WindowPtr wind)
  4242. {
  4243.     TWindow **info;
  4244.     short curField;
  4245.     TMsgFieldInfo **fields;
  4246.     TEHandle edit;
  4247.     
  4248.     info = (TWindow**)GetWRefCon(wind);
  4249.     curField = (**info).curField;
  4250.     fields = (**info).fields;
  4251.     edit = (*fields)[curField].edit;
  4252.     TESetSelect(0, 0x7fff, edit);
  4253. }
  4254.  
  4255.  
  4256.  
  4257. /*----------------------------------------------------------------------------
  4258.     DoFind 
  4259.     
  4260.     Handle the "Find" command for a message window.
  4261.             
  4262.     Entry:    wind = pointer to message window.
  4263.     
  4264.     Exit:    function result = error code.
  4265. ----------------------------------------------------------------------------*/
  4266.  
  4267. static OSErr DoFind (WindowPtr wind)
  4268. {
  4269.     TWindow **info;
  4270.     OSErr err = noErr;
  4271.     short curField;
  4272.     TMsgFieldInfo **fields;
  4273.     TEHandle edit;
  4274.     
  4275.     err = DoFindDialog();
  4276.     if (err != noErr) return err;
  4277.     info = (TWindow**)GetWRefCon(wind);
  4278.     curField = (**info).curField;
  4279.     fields = (**info).fields;
  4280.     edit = (*fields)[curField].edit;
  4281.     return Find(wind, gPrefs.startFindAtBeginning ? 0 : (**edit).selStart);
  4282. }
  4283.  
  4284.  
  4285.  
  4286. /*----------------------------------------------------------------------------
  4287.     DoFindAgain
  4288.     
  4289.     Handle the "Find Again" command for a message window.
  4290.             
  4291.     Entry:    wind = pointer to message window.
  4292.     
  4293.     Exit:    function result = error code.
  4294. ----------------------------------------------------------------------------*/
  4295.  
  4296. static OSErr DoFindAgain (WindowPtr wind)
  4297. {
  4298.     TWindow **info;
  4299.     short curField;
  4300.     TMsgFieldInfo **fields;
  4301.     TEHandle edit;
  4302.     
  4303.     info = (TWindow**)GetWRefCon(wind);
  4304.     curField = (**info).curField;
  4305.     fields = (**info).fields;
  4306.     edit = (*fields)[curField].edit;
  4307.     return Find(wind, (**edit).selEnd);
  4308. }
  4309.  
  4310.  
  4311.  
  4312. /*----------------------------------------------------------------------------
  4313.     DoEnterSelection
  4314.     
  4315.     Handle the "Enter Selection" command for a message window.
  4316.             
  4317.     Entry:    wind = pointer to message window.
  4318.     
  4319.     Exit:    function result = error code.
  4320. ----------------------------------------------------------------------------*/
  4321.  
  4322. static OSErr DoEnterSelection (WindowPtr wind)
  4323. {
  4324.     TWindow **info;
  4325.     short curField;
  4326.     TMsgFieldInfo **fields;
  4327.     TEHandle edit;
  4328.     short selStart, selEnd, len;
  4329.     Handle hText;
  4330.     
  4331.     info = (TWindow**)GetWRefCon(wind);
  4332.     curField = (**info).curField;
  4333.     fields = (**info).fields;
  4334.     edit = (*fields)[curField].edit;
  4335.     selStart = (**edit).selStart;
  4336.     selEnd = (**edit).selEnd;
  4337.     hText = (**edit).hText;
  4338.     if (selStart >= selEnd || selEnd > selStart + 255) return noErr;
  4339.     len = selEnd - selStart;
  4340.     BlockMoveData(*hText + selStart, gFindPattern, len);
  4341.     gFindPattern[len] = 0;
  4342.     return noErr;
  4343. }
  4344.  
  4345.  
  4346.  
  4347. /*----------------------------------------------------------------------------
  4348.     DoShowHideDetails 
  4349.     
  4350.     Handle the "Show/Hide Details" command.
  4351.             
  4352.     Entry:    wind = pointer to active message window.
  4353. ----------------------------------------------------------------------------*/
  4354.  
  4355. static void DoShowHideDetails (WindowPtr wind)
  4356. {
  4357.     TWindow **info;
  4358.     Boolean showDetails;
  4359.     short iconPanelHeight, panelHeight, width, height;
  4360.     ControlHandle vScroll, tabCheckbox, wrapCheckbox;
  4361.     Rect r;
  4362.     
  4363.     info = (TWindow**)GetWRefCon(wind);
  4364.     showDetails = !(**info).showDetails;
  4365.     (**info).showDetails = showDetails;
  4366.     iconPanelHeight = panelHeight = (**info).iconPanelHeight;
  4367.     if (showDetails) panelHeight += (**info).optionsPanelHeight;
  4368.     (**info).panelHeight = panelHeight;
  4369.     r = wind->portRect;
  4370.     width = r.right;
  4371.     height = r.bottom;
  4372.     r.top = iconPanelHeight;
  4373.     r.bottom -= 15;
  4374.     EraseRect(&r);
  4375.     InvalRect(&r);
  4376.     vScroll = (**info).vScroll;
  4377.     SetRect(&r, width-15, panelHeight-1, width+1, height-14);
  4378.     (**vScroll).contrlRect = r;
  4379.     tabCheckbox = (**info).tabCheckbox;
  4380.     wrapCheckbox = (**info).wrapCheckbox;
  4381.     (**tabCheckbox).contrlVis = showDetails;
  4382.     (**wrapCheckbox).contrlVis = showDetails;
  4383.     ChangeDisplayedFields(wind);
  4384. }
  4385.  
  4386.  
  4387.  
  4388. /*----------------------------------------------------------------------------
  4389.     DoRot13 
  4390.     
  4391.     Handle the "Rot13" command.
  4392.             
  4393.     Entry:    wind = pointer to active message window.
  4394. ----------------------------------------------------------------------------*/
  4395.  
  4396. static void DoRot13 (WindowPtr wind)
  4397. {
  4398.     TWindow **info;
  4399.     short curField;
  4400.     TMsgFieldInfo **fields;
  4401.     TEHandle edit;
  4402.     Rect r;
  4403.     
  4404.     info = (TWindow**)GetWRefCon(wind);
  4405.     curField = (**info).curField;
  4406.     if (curField < (**info).firstScrollingField) return;
  4407.     fields = (**info).fields;
  4408.     edit = (*fields)[curField].edit;
  4409.     Rot13Text((**edit).hText, (**edit).selStart, (**edit).selEnd);
  4410.     r = (**edit).viewRect;
  4411.     InvalRect(&r);
  4412.     (**info).changed = true;
  4413. }
  4414.  
  4415.  
  4416.  
  4417. /*----------------------------------------------------------------------------
  4418.     DoInsertSpoilerCharacter 
  4419.     
  4420.     Handle the "Insert Spoiler Character" command.
  4421.             
  4422.     Entry:    wind = pointer to active message window.
  4423. ----------------------------------------------------------------------------*/
  4424.  
  4425. static void DoInsertSpoilerCharacter (WindowPtr wind)
  4426. {
  4427.     TWindow **info;
  4428.     short curField;
  4429.     TMsgFieldInfo **fields;
  4430.     TEHandle edit;
  4431.     
  4432.     info = (TWindow**)GetWRefCon(wind);
  4433.     curField = (**info).curField;
  4434.     fields = (**info).fields;
  4435.     edit = (*fields)[curField].edit;
  4436.     if (edit != (**info).theTE) {
  4437.         SysBeep(0);
  4438.         return;
  4439.     }
  4440.     if ((long)(**edit).teLength - (long)(**edit).selEnd + 
  4441.             (long)(**edit).selStart + 1 > 0x7fff) goto exit;
  4442.     TEKey(FF, edit);
  4443.     AdjustCurFieldHeight(wind);
  4444.     (**info).changed = true;
  4445.     return;
  4446.     
  4447. exit:
  4448.  
  4449.     ErrorMessageNumber(kStrNoMoreChars);
  4450.     return;
  4451. }
  4452.  
  4453.  
  4454.  
  4455. /*----------------------------------------------------------------------------
  4456.     DoWrap 
  4457.     
  4458.     Handle the "Wrap" command.
  4459.             
  4460.     Entry:    wind = pointer to active message window.
  4461. ----------------------------------------------------------------------------*/
  4462.  
  4463. static void DoWrap (WindowPtr wind)
  4464. {
  4465.     TWindow **info;
  4466.     short curField;
  4467.     TMsgFieldInfo **fields;
  4468.     TEHandle edit;
  4469.     Rect r;
  4470.     
  4471.     info = (TWindow**)GetWRefCon(wind);
  4472.     curField = (**info).curField;
  4473.     if (curField < (**info).firstScrollingField) return;
  4474.     fields = (**info).fields;
  4475.     edit = (*fields)[curField].edit;
  4476.     Wrap((**edit).hText, (**edit).selStart, (**edit).selEnd);
  4477.     TECalText(edit);
  4478.     r = (**edit).viewRect;
  4479.     InvalRect(&r);
  4480.     AdjustCurFieldHeight(wind);
  4481.     (**info).changed = true;
  4482. }
  4483.  
  4484.  
  4485.  
  4486. /*----------------------------------------------------------------------------
  4487.     DoUnwrap 
  4488.     
  4489.     Handle the "Unwrap" command.
  4490.             
  4491.     Entry:    wind = pointer to active message window.
  4492. ----------------------------------------------------------------------------*/
  4493.  
  4494. static void DoUnwrap (WindowPtr wind)
  4495. {
  4496.     TWindow **info;
  4497.     short curField;
  4498.     TMsgFieldInfo **fields;
  4499.     TEHandle edit;
  4500.     Rect r;
  4501.     
  4502.     info = (TWindow**)GetWRefCon(wind);
  4503.     curField = (**info).curField;
  4504.     if (curField < (**info).firstScrollingField) return;
  4505.     fields = (**info).fields;
  4506.     edit = (*fields)[curField].edit;
  4507.     UnWrap((**edit).hText, (**edit).selStart, (**edit).selEnd);
  4508.     TECalText(edit);
  4509.     r = (**edit).viewRect;
  4510.     InvalRect(&r);
  4511.     AdjustCurFieldHeight(wind);
  4512.     (**info).changed = true;
  4513.     return;
  4514. }
  4515.  
  4516.  
  4517.  
  4518. /*----------------------------------------------------------------------------
  4519.     DoSendMessage 
  4520.     
  4521.     Handles the "Send Message" command.
  4522.             
  4523.     Entry:    wind = pointer to message window.
  4524.     
  4525.     Exit:    function result = error code.
  4526. ----------------------------------------------------------------------------*/
  4527.  
  4528. static OSErr DoSendMessage (WindowPtr wind)
  4529. {
  4530.     TWindow **info;
  4531.     ControlHandle sendButton;
  4532.     long myticks;
  4533.  
  4534.     info = (TWindow**)GetWRefCon(wind);
  4535.     sendButton = (**info).sendButton;
  4536.     HiliteControl(sendButton, 1);
  4537.     Delay(8, &myticks);
  4538.     HiliteControl(sendButton, 0);
  4539.     return SendMessageAndCloseWindow(wind);
  4540. }
  4541.  
  4542.  
  4543.  
  4544. /*----------------------------------------------------------------------------
  4545.     Activate 
  4546.     
  4547.     Handle an activate event for a message window.
  4548.             
  4549.     Entry:    wind = pointer to message window.
  4550.             act = true to activate, false to deactivate.
  4551.             
  4552.     Exit:    function result = error code.
  4553. ----------------------------------------------------------------------------*/
  4554.  
  4555. static void Activate (WindowPtr wind, Boolean act)
  4556. {
  4557.     TWindow **info;
  4558.     TMsgFieldInfo **fields;
  4559.     short curField;
  4560.     TEHandle edit;
  4561.     Rect r;
  4562.  
  4563.     info = (TWindow**)GetWRefCon(wind);
  4564.     curField = (**info).curField;
  4565.     fields = (**info).fields;
  4566.     edit = (*fields)[curField].edit;
  4567.     if (act) {
  4568.         ShowControl((**info).vScroll);
  4569.         MyTEActivate(edit);
  4570.     } else {
  4571.         HideControl((**info).vScroll);
  4572.         TEDeactivate(edit);
  4573.     }
  4574.     r = wind->portRect;
  4575.     r.top = r.bottom - 15;
  4576.     r.left = r.right - 15;
  4577.     InvalRect(&r);
  4578.     HandleUpdate(wind);
  4579. }
  4580.  
  4581.  
  4582.  
  4583. /*----------------------------------------------------------------------------
  4584.     Update 
  4585.     
  4586.     Handle an update event for a message window.
  4587.             
  4588.     Entry:    wind = pointer to message window.
  4589. ----------------------------------------------------------------------------*/
  4590.  
  4591. static void Update (WindowPtr wind)
  4592. {
  4593.     TWindow **info;
  4594.     short panelHeight, windWidth;
  4595.     Rect r, textRect;
  4596.     TMsgFieldInfo **fields, *f;
  4597.     short numFields, i;
  4598.     short updateTop, updateBottom, lineHeight;
  4599.     short v;
  4600.     TEHandle edit;
  4601.     FontInfo fInfo;
  4602.     short labelWidth;
  4603.     Str255 tabStopsStr, quoteStr;
  4604.     char state;
  4605.     Boolean showLabels;
  4606.  
  4607.     info = (TWindow**)GetWRefCon(wind);
  4608.     panelHeight = (**info).panelHeight;
  4609.     GetFontInfo(&fInfo);
  4610.     
  4611.     r = wind->portRect;
  4612.     r.top += panelHeight;
  4613.     ClipRect(&r);
  4614.     DrawGrowIcon(wind);
  4615.     ClipRect(&wind->portRect);
  4616.     
  4617.     UpdateControls(wind, wind->visRgn);
  4618.     
  4619.     showLabels = (**info).showLabels;
  4620.     DrawIcon(kNewsIconH, kNewsIconID, (**info).newsIcon, showLabels, kStrNewsIconLabel);
  4621.     DrawIcon(kMailIconH, kMailIconID, (**info).mailIcon, showLabels, kStrMailIconLabel);
  4622.     DrawIcon(kSelfIconH, kSelfIconID, (**info).selfIcon, showLabels, kStrSelfIconLabel);
  4623.     
  4624.     windWidth = wind->portRect.right;
  4625.     v = (**info).iconPanelHeight - 3;
  4626.     MoveTo(0, v);
  4627.     LineTo(windWidth, v);
  4628.     v += 2;
  4629.     MoveTo(0, v);
  4630.     LineTo(windWidth, v);
  4631.     
  4632.     if ((**info).showDetails) {
  4633.     
  4634.         GetPString(kStrTabStops, tabStopsStr);
  4635.         edit = (**info).tabField;
  4636.         r = (**edit).viewRect;
  4637.         InsetRect(&r, -3, -3);
  4638.         FrameRect(&r);
  4639.         r = (**edit).viewRect;
  4640.         r.right = r.left - 3;
  4641.         r.left = r.right - StringWidth(tabStopsStr) - 5;
  4642.         TETextBox(tabStopsStr+1, *tabStopsStr, &r, teFlushRight);
  4643.         TEUpdate(&wind->portRect, edit);
  4644.     
  4645.         GetPString(kStrQuoteString, quoteStr);
  4646.         edit = (**info).quoteStringField;
  4647.         r = (**edit).viewRect;
  4648.         InsetRect(&r, -3, -3);
  4649.         FrameRect(&r);
  4650.         r = (**edit).viewRect;
  4651.         r.right = r.left - 3;
  4652.         r.left = r.right - StringWidth(quoteStr) - 5;
  4653.         TETextBox(quoteStr+1, *quoteStr, &r, teFlushRight);
  4654.         TEUpdate(&wind->portRect, edit);
  4655.     
  4656.         v = (**info).iconPanelHeight + (**info).optionsPanelHeight - 3;
  4657.         MoveTo(0, v);
  4658.         LineTo(windWidth, v);
  4659.         v += 2;
  4660.         MoveTo(0, v);
  4661.         LineTo(windWidth, v);
  4662.         
  4663.     }
  4664.     
  4665.     fields = (**info).fields;
  4666.     numFields = (**info).numFields;
  4667.     updateTop = (**wind->visRgn).rgnBBox.top;
  4668.     updateBottom = (**wind->visRgn).rgnBBox.bottom;
  4669.     GetTextRect(wind, &textRect);
  4670.     GetViewRect(wind, &r);
  4671.     state = MyHGetState(fields);
  4672.     MyHLock(fields);
  4673.     for (i = (**info).firstScrollingField, f = *fields + i; i < numFields; i++, f++) {
  4674.         edit = f->edit;
  4675.         lineHeight = (**edit).lineHeight;
  4676.         if (f->bottom < updateTop || f->top > updateBottom) continue;
  4677.         v = f->top;
  4678.         if (f->sepLine) {
  4679.             if (v >= textRect.top && v < textRect.bottom) {
  4680.                 r.top = v + (lineHeight >> 1);
  4681.                 r.bottom = r.top + 1;
  4682.                 r.left = 0;
  4683.                 r.right = windWidth - 15;
  4684.                 DrawGrayRect(&r);
  4685.             }
  4686.             v += lineHeight;
  4687.         }
  4688.         if (v >= textRect.top && v < textRect.bottom) {
  4689.             if (f->labelKind == kMsgFieldLabelLeft) {
  4690.                 labelWidth = StringWidth(f->label);
  4691.                 r = (**edit).viewRect;
  4692.                 r.right = r.left;
  4693.                 r.left = 0;
  4694.                 TETextBox(f->label+1, *f->label, &r, teFlushRight);
  4695.             } else if (f->labelKind == kMsgFieldLabelTop) {
  4696.                 MoveTo(kTextMargin, v + fInfo.leading + fInfo.ascent);
  4697.                 TextFace(italic);
  4698.                 DrawString(f->label);
  4699.                 TextFace(0);
  4700.             }
  4701.         }
  4702.         TEUpdate(&wind->portRect, edit);
  4703.     }
  4704.     MyHSetState(fields, state);
  4705. }
  4706.  
  4707.  
  4708.  
  4709. /*----------------------------------------------------------------------------
  4710.     Mouse 
  4711.     
  4712.     Handle a mouse down event in the content area of a message window.
  4713.             
  4714.     Entry:    wind = pointer to message window.
  4715.             where = location of mouse down in local coords.
  4716.             modifiers = modifiers field from event record.
  4717.             
  4718.     Exit:    function result = error code.
  4719. ----------------------------------------------------------------------------*/
  4720.  
  4721. static OSErr Mouse (WindowPtr wind, Point where, short modifiers)
  4722. {
  4723.     TWindow **info;
  4724.     short part, dv, oldVal;
  4725.     ControlHandle control;
  4726.     Rect r, iconRect;
  4727.     Boolean iconClick = false;
  4728.     Boolean changeDisplayedFields = false;
  4729.     TEHandle edit;
  4730.     OSErr err = noErr;
  4731.     short iconID;
  4732.     Boolean dragged = false, trashed, extraSpaceDeleted;
  4733.     short fieldIndex;
  4734.     short oldSelStart, oldSelEnd;
  4735.  
  4736.     info = (TWindow**)GetWRefCon(wind);
  4737.     
  4738.     part = FindControl(where, wind, &control);
  4739.     if (part != 0) {
  4740.         if (control == (**info).sendButton) {
  4741.             if (TrackControl(control, where, nil) != 0) {
  4742.                 err = SendMessageAndCloseWindow(wind);
  4743.                 if (err != noErr) return err;
  4744.             }
  4745.         } else if (control == (**info).vScroll) {
  4746.             if (part == inThumb) {
  4747.                 oldVal = GetControlValue(control);
  4748.                 TrackControl(control, where, nil);
  4749.                 dv = GetControlValue(control) - oldVal;
  4750.                 if (dv != 0) Scroll(wind, -dv);
  4751.             } else {
  4752.                 SetControlReference(control, 0);
  4753.                 TrackControl(control, where, gScrollActionUPP);
  4754.                 SetControlReference(control, 1);
  4755.                 AdjustScrollMax(wind);
  4756.             }
  4757.         } else if (TrackControl(control, where, nil) != 0) {
  4758.             oldVal = GetControlValue(control);
  4759.             SetControlValue(control, 1 - oldVal);
  4760.             if (control == (**info).tabCheckbox) {
  4761.                 (**info).tabEnabled = oldVal == 0;
  4762.                 KillBalloon();
  4763.             } else if (control == (**info).wrapCheckbox) {
  4764.                 (**info).wrapOnSend = oldVal == 0;
  4765.                 KillBalloon();
  4766.             }
  4767.             (**info).changed = true;
  4768.         }
  4769.      } else if (where.v < (**info).iconPanelHeight) {
  4770.          SetRect(&iconRect, kNewsIconH, kIconV, kNewsIconH + 32, kIconV + 32);
  4771.          if ((**info).showLabels) {
  4772.              SetRect(&r, kNewsIconH - 24, 0, 
  4773.                  kNewsIconH + 52, (**info).iconPanelHeight - 3);
  4774.          } else {
  4775.              SetRect(&r, kNewsIconH - kCheckMarkDeltaH, 0, 
  4776.                  kNewsIconH + 32, (**info).iconPanelHeight - 3);
  4777.          }
  4778.          if (PtInRect(where, &r)) {
  4779.              iconClick = true;
  4780.              iconID = kNewsIconID;
  4781.          } else {
  4782.              OffsetRect(&r, kMailIconH - kNewsIconH, 0);
  4783.              OffsetRect(&iconRect, kMailIconH - kNewsIconH, 0);
  4784.              if (PtInRect(where, &r)) {
  4785.                  iconClick = true;
  4786.                  iconID = kMailIconID;
  4787.              } else {
  4788.                  OffsetRect(&r, kSelfIconH - kMailIconH, 0);
  4789.                  OffsetRect(&iconRect, kSelfIconH - kMailIconH, 0);
  4790.                  if (PtInRect(where, &r)) {
  4791.                      iconClick = true;
  4792.                      iconID = kSelfIconID;
  4793.                  }
  4794.              }
  4795.          }
  4796.          if (iconClick && TrackIconClick(where, &iconRect, &r, iconID)) {
  4797.              switch (iconID) {
  4798.                  case kNewsIconID:
  4799.                      (**info).newsIcon = !(**info).newsIcon;
  4800.                      changeDisplayedFields = true;
  4801.                      break;
  4802.                  case kMailIconID:
  4803.                      (**info).mailIcon = !(**info).mailIcon;
  4804.                      changeDisplayedFields = true;
  4805.                      break;
  4806.                  case kSelfIconID:
  4807.                      (**info).selfIcon = !(**info).selfIcon;
  4808.                      changeDisplayedFields = false;
  4809.                      break;
  4810.              }
  4811.              KillBalloon();
  4812.              r = iconRect;
  4813.              r.left -= kCheckMarkDeltaH;
  4814.              r.right = iconRect.left - 1;
  4815.              InvalRect(&r);
  4816.              HiliteControl((**info).sendButton, 
  4817.                  (**info).newsIcon || (**info).mailIcon || (**info).selfIcon ? 0 : 255);
  4818.              if (changeDisplayedFields) ChangeDisplayedFields(wind);
  4819.              (**info).changed = true;
  4820.          }
  4821.     } else {
  4822.         edit = FindFieldContainingPoint(wind, where, &fieldIndex);
  4823.         if (edit != nil) {
  4824.             if (gHaveDragMgr) {
  4825.                 err = DragText(&gCurEvent, where, edit, &dragged, &trashed);
  4826.                 if (err != noErr) return err;
  4827.             }
  4828.             if (wind == FrontWindow() && !dragged) {
  4829.                 if (fieldIndex != (**info).curField) {
  4830.                     ChangeCurField(wind, fieldIndex);
  4831.                     modifiers = 0;
  4832.                 }
  4833.                 oldSelStart = (**edit).selStart;
  4834.                 oldSelEnd = (**edit).selEnd;
  4835.                 MyTEClick(where, (modifiers & shiftKey) != 0, edit);
  4836.                 if (fieldIndex >= (**info).firstScrollingField) {
  4837.                     err = CommandClick(wind, edit, oldSelStart, oldSelEnd, modifiers);
  4838.                     if (err != noErr) return err;
  4839.                 }
  4840.             } else if (trashed) {
  4841.                 MyTEDelete(edit, false, &extraSpaceDeleted);
  4842.                 AdjustFieldHeight(wind, edit);
  4843.                 (**info).changed = true;
  4844.             }
  4845.         }
  4846.     }
  4847.     return noErr;
  4848. }
  4849.  
  4850.  
  4851.  
  4852. /*----------------------------------------------------------------------------
  4853.     Draggable
  4854.     
  4855.     Determine whether a mouse down event is on a draggable object in a 
  4856.     message window.
  4857.     
  4858.     Entry:    wind = pointer to message window.
  4859.             where = location of mouse down event, in local coordinates.
  4860.             modifiers = modifiers field from event record.
  4861.             
  4862.     Exit:    function result = true if object is draggable.
  4863. ----------------------------------------------------------------------------*/
  4864.  
  4865. static Boolean Draggable (WindowPtr wind, Point where, short modifiers)
  4866. {
  4867.     TWindow **info;
  4868.     TMsgFieldInfo **fields;
  4869.     short curField;
  4870.     TEHandle edit;
  4871.     
  4872.     info = (TWindow**)GetWRefCon(wind);
  4873.     fields = (**info).fields;
  4874.     curField = (**info).curField;
  4875.     edit = (*fields)[curField].edit;
  4876.     return PtInTEHiliteRgn(where, edit);
  4877. }
  4878.  
  4879.  
  4880.  
  4881. /*----------------------------------------------------------------------------
  4882.     Key 
  4883.     
  4884.     Handle a key down event for a message window.
  4885.             
  4886.     Entry:    wind = pointer to message window.
  4887.             theChar = ASCII code of key.
  4888.             theKey = keyboard code of key.
  4889.             modifiers = modifiers field from event record.
  4890.             
  4891.     Exit:    function result = error code.
  4892. ----------------------------------------------------------------------------*/
  4893.  
  4894. static OSErr Key (WindowPtr wind, unsigned char theChar, unsigned char theKey, 
  4895.     short modifiers)
  4896. {
  4897.     TWindow **info;
  4898.     TMsgFieldInfo **fields;
  4899.     TEHandle edit;
  4900.     short curField, numFields;
  4901.     short selStart, selEnd, teLength;
  4902.     Handle text;
  4903.     short h;
  4904.     Rect r;
  4905.     short numSpace;
  4906.     char *p;
  4907.     short col;
  4908.     Boolean shift, option, command, tabEnabled;
  4909.     ControlHandle vScroll;
  4910.     short tabStops;
  4911.     long maxLen;
  4912.     Boolean isTabField, isQuoteStringField, isBodyField, isSigField, isArrow;
  4913.     OSErr err = noErr;
  4914.     Boolean extraSpaceDeleted;
  4915.     short scrollIntoView;
  4916.     
  4917.     shift = (modifiers & shiftKey) != 0;
  4918.     option = (modifiers & optionKey) != 0;
  4919.     command = (modifiers & cmdKey) != 0;
  4920.  
  4921.     info = (TWindow**)GetWRefCon(wind);
  4922.     vScroll = (**info).vScroll;
  4923.     fields = (**info).fields;
  4924.     curField = (**info).curField;
  4925.     numFields = (**info).numFields;
  4926.     tabEnabled = (**info).tabEnabled;
  4927.     tabStops = (**info).tabStops;
  4928.     edit = (*fields)[curField].edit;
  4929.     selStart = (**edit).selStart;
  4930.     selEnd = (**edit).selEnd;
  4931.     teLength = (**edit).teLength;
  4932.     text = (**edit).hText;
  4933.     
  4934.     isTabField = edit == (**info).tabField;
  4935.     isQuoteStringField = edit == (**info).quoteStringField;
  4936.     isBodyField = edit == (**info).theTE;
  4937.     isSigField = edit == (**info).signatureField;
  4938.     isArrow = IsArrowKey(theChar);
  4939.  
  4940.     if (command) {
  4941.         if (theChar == '1' || theChar == '2' || theChar == '3') {
  4942.             switch (theChar) {
  4943.                 case '1':
  4944.                     (**info).newsIcon = !(**info).newsIcon;
  4945.                     h = kNewsIconH;
  4946.                     ChangeDisplayedFields(wind);
  4947.                     break;
  4948.                 case '2':
  4949.                     (**info).mailIcon = !(**info).mailIcon;
  4950.                     ChangeDisplayedFields(wind);
  4951.                     h = kMailIconH;
  4952.                     break;
  4953.                 case '3':
  4954.                     (**info).selfIcon = !(**info).selfIcon;
  4955.                     h = kSelfIconH;
  4956.                     break;
  4957.             }
  4958.             SetRect(&r, h - kCheckMarkDeltaH, kIconV, h - 1, kIconV + 32);
  4959.             InvalRect(&r);
  4960.              HiliteControl((**info).sendButton, 
  4961.                  (**info).newsIcon || (**info).mailIcon || (**info).selfIcon ? 0 : 255);
  4962.              (**info).changed = true;
  4963.              KillBalloon();
  4964.              return noErr;
  4965.         } else if (!isArrow) {
  4966.             SysBeep(0);
  4967.             return noErr;
  4968.         }
  4969.     }
  4970.  
  4971.     /* Note: The following code has been commented out to disable the keypad shortcuts
  4972.        in message windows, at the request of J.P. Kuypers, who would like to use the
  4973.        keypad to type numbers on his Azerty keyboard. If anyone complains, I can add
  4974.        a new pref for this. */
  4975.        
  4976.     /*
  4977.         if (gPrefs.keypadShortcuts && IsKeypadKey(theChar, theKey, &keypadKey)) {
  4978.             switch (keypadKey) {
  4979.                 case kKeypadEqualKey:
  4980.                     DoSelectAll(wind);
  4981.                     return noErr;
  4982.                 case kKeypadStarKey:
  4983.                     return DoClose(wind);
  4984.                 case kKeypad1Key:
  4985.                     ScrollAction(vScroll, kScrollToEnd);
  4986.                     return noErr;
  4987.                 case kKeypad2Key:
  4988.                     ScrollAction(vScroll, inDownButton);
  4989.                     return noErr;
  4990.                 case kKeypad3Key:
  4991.                     ScrollAction(vScroll, inPageDown);
  4992.                     return noErr;
  4993.                 case kKeypad7Key:
  4994.                     ScrollAction(vScroll, kScrollToHome);
  4995.                     return noErr;
  4996.                 case kKeypad8Key:
  4997.                     ScrollAction(vScroll, inUpButton);
  4998.                     return noErr;
  4999.                 case kKeypad9Key:
  5000.                     ScrollAction(vScroll, inPageUp);
  5001.                     return noErr;
  5002.                 default:
  5003.                     SysBeep(0);
  5004.                     return noErr;
  5005.             }
  5006.         }
  5007.     */
  5008.     
  5009.     if (theChar == 0x0C && theKey != 0x79 && (modifiers & controlKey) != 0) {
  5010.         /* Control-L */
  5011.         DoInsertSpoilerCharacter(wind);
  5012.         return noErr;
  5013.     }
  5014.     
  5015.     if (theChar == pageUpKey) {
  5016.         ScrollAction(vScroll, inPageUp);
  5017.         return noErr;
  5018.     }
  5019.     if (theChar == pageDownKey) {
  5020.         ScrollAction(vScroll, inPageDown);
  5021.         return noErr;
  5022.     }
  5023.     if (theChar == homeKey) {
  5024.         ScrollAction(vScroll, kScrollToHome);
  5025.         return noErr;
  5026.     }
  5027.     if (theChar == endKey) {
  5028.         ScrollAction(vScroll, kScrollToEnd);
  5029.         return noErr;
  5030.     }
  5031.     
  5032.     if (theChar == forwardDelKey) {
  5033.         if (selStart < selEnd) {
  5034.             theChar = deleteKey;
  5035.         } else if (selEnd < teLength) {
  5036.             (**edit).selEnd = selEnd + 1;
  5037.             TEDelete(edit);
  5038.             goto exit1;
  5039.         } else {
  5040.             SysBeep(0);
  5041.             return noErr;
  5042.         }
  5043.     }
  5044.     
  5045.     if (theChar == deleteKey && selStart < selEnd) {
  5046.         MyTEDelete(edit, false, &extraSpaceDeleted);
  5047.         goto exit1;
  5048.     }
  5049.     
  5050.     if (theChar == tabKey) {
  5051.         if (tabEnabled && !shift && !option && (isBodyField || isSigField)) {
  5052.             if (tabStops == 0) {
  5053.                 numSpace = 1;
  5054.             } else {
  5055.                 p = *text + selStart - 1;
  5056.                 col = 0;
  5057.                 while (p >= *text && *p != CR) {
  5058.                     p--;
  5059.                     col++;
  5060.                 }
  5061.                 numSpace = tabStops - (col % tabStops);
  5062.             }
  5063.             if ((long)(**edit).teLength - (long)(**edit).selEnd + 
  5064.                 (long)(**edit).selStart + numSpace > 0x7fff) goto exit2;
  5065.             while (numSpace--) TEKey(' ', edit);
  5066.             goto exit1;
  5067.         } else {
  5068.             if (shift) {
  5069.                 curField--;
  5070.                 if (curField < 0) curField = numFields - 1;
  5071.             } else {
  5072.                 curField++;
  5073.                 if (curField >= numFields) curField = 0;
  5074.             }
  5075.             edit = (*fields)[curField].edit;
  5076.             TESetSelect(0, 0x7fff, edit);
  5077.             ChangeCurField(wind, curField);
  5078.             ScrollSelectionIntoView(wind);
  5079.             return noErr;
  5080.         }
  5081.     }
  5082.     
  5083.     if (theChar != deleteKey && !isArrow) {
  5084.         if (!isPrintable(theChar) && theChar != CR ||
  5085.             theChar == CR && (isTabField || isQuoteStringField) ||
  5086.             isTabField && !(theChar >= '0' && theChar <= '9'))
  5087.         {
  5088.             SysBeep(0);
  5089.             return noErr;
  5090.         }
  5091.     }
  5092.     
  5093.     maxLen = isTabField ? 2 : isQuoteStringField ? 11 : 0x7fff;
  5094.     if (isPrintable(theChar) || theChar == CR) {
  5095.         if ((long)(**edit).teLength - (long)(**edit).selEnd + 
  5096.             (long)(**edit).selStart + 1 > maxLen) goto exit2;
  5097.     }
  5098.     
  5099.     if (isArrow) {
  5100.         TEArrowKey(theChar, modifiers, edit, GetPageHeight(wind), &gPrevEvent, 
  5101.             &scrollIntoView);
  5102.         ScrollRangeIntoView(wind, scrollIntoView, scrollIntoView);
  5103.     } else {
  5104.         TEKey(theChar, edit);
  5105.     }
  5106.     
  5107. exit1:
  5108.     
  5109.     if (!isArrow) {
  5110.         AdjustCurFieldHeight(wind);
  5111.         (**info).changed = true;
  5112.     }
  5113.     return noErr;
  5114.     
  5115. exit2:
  5116.  
  5117.     if (!isTabField && !isQuoteStringField) {
  5118.         ErrorMessageNumber(kStrNoMoreChars);
  5119.     } else {
  5120.         ErrorMessageNumber(kStrNoMoreTyping);
  5121.     }
  5122.     return userCanceledErr;
  5123. }
  5124.  
  5125.  
  5126.  
  5127. /*----------------------------------------------------------------------------
  5128.     Grow 
  5129.     
  5130.     Handle a mouse down event in the grow box of a message window.
  5131.     
  5132.     Entry:    wind = pointer to message window.
  5133.             where = location of mouse down event, in global coordinates.
  5134.             
  5135.     Exit:    function result = error code.
  5136. ----------------------------------------------------------------------------*/
  5137.  
  5138. static OSErr Grow (WindowPtr wind, Point where)
  5139. {
  5140.     Rect sizeRect;
  5141.     long size;
  5142.     short width, height;
  5143.     TWindow **info;
  5144.     
  5145.     SetRect(&sizeRect, MinWidth(wind), MinHeight(wind), 0x7fff, 0x7fff);
  5146.     size = GrowWindow(wind, where, &sizeRect);
  5147.     
  5148.     if (size  != 0) {
  5149.         width = LoWord(size);
  5150.         height = HiWord(size);
  5151.         FixHeight(wind, &height);
  5152.         SizeWindow(wind, width, height, false);
  5153.         ResizeContents(wind);
  5154.         info = (TWindow**)GetWRefCon(wind);
  5155.         (**info).windPosValid = true;
  5156.         (**info).movedSinceLastSave = true;
  5157.     }
  5158.     
  5159.     return noErr;
  5160. }
  5161.  
  5162.  
  5163.  
  5164. /*----------------------------------------------------------------------------
  5165.     Zoom
  5166.     
  5167.     Zoom a message window.
  5168.     
  5169.     Entry:    wind = pointer to message window.
  5170.             zoomDir = zoom direction = inZoomIn or inZoomOut.
  5171.             
  5172.     Exit:    function result = error code.
  5173. ----------------------------------------------------------------------------*/
  5174.  
  5175. static OSErr Zoom (WindowPtr wind, short zoomDir)
  5176. {
  5177.     TWindow **info;
  5178.     short width, height;
  5179.     Rect zoomRect;    
  5180.     WStateData **wState;
  5181.  
  5182.     info = (TWindow**)GetWRefCon(wind);
  5183.     wState = (WStateData**)((WindowPeek)wind)->dataHandle;
  5184.     if (zoomDir == inZoomOut) {
  5185.         width = 80*CharWidth('W') + 2*kTextMargin + 18;
  5186.         height = 0x7fff;
  5187.         CalculateZoomRect(wind, width, height, &zoomRect, gPrefs.dontCoverFinderIcons);
  5188.         height = zoomRect.bottom - zoomRect.top;
  5189.         FixHeight(wind, &height);
  5190.         zoomRect.bottom = zoomRect.top + height;
  5191.         (**wState).stdState = zoomRect;
  5192.         if (WindRectEqualRect(wind, &zoomRect)) return noErr;
  5193.     }
  5194.     
  5195.     EraseRect(&wind->portRect);
  5196.     ZoomWindow(wind, zoomDir, false);
  5197.     ResizeContents(wind);
  5198.     (**info).windPosValid = true;
  5199.     (**info).movedSinceLastSave = true;
  5200.     return noErr;
  5201. }
  5202.  
  5203.  
  5204.  
  5205. /*----------------------------------------------------------------------------
  5206.     Command 
  5207.     
  5208.     Handle a command for a message window.
  5209.             
  5210.     Entry:    wind = pointer to message window.
  5211.             menu = the menu.
  5212.             item = the item.
  5213.             modifiers = modifiers field from event record.
  5214.     
  5215.     Exit:    function result = error code.
  5216. ----------------------------------------------------------------------------*/
  5217.  
  5218. static OSErr Command (WindowPtr wind, short menu, short item, short modifiers)
  5219. {
  5220.     OSErr err = noErr;
  5221.  
  5222.     switch (menu) {
  5223.                 
  5224.         case kFileMenu:
  5225.         
  5226.             switch (item) {
  5227.                 case kSaveItem:
  5228.                     err = DoSave(wind, modifiers);
  5229.                     break;
  5230.                 case kSaveAsItem:
  5231.                     err = DoSaveAs(wind);
  5232.                     break;
  5233.                 case kPrintItem:
  5234.                     err = DoPrint(wind, modifiers);
  5235.                     break;
  5236.             }
  5237.             break;
  5238.             
  5239.         case kEditMenu:
  5240.  
  5241.             switch (item) {
  5242.                 case kCutItem:
  5243.                     DoCut(wind);
  5244.                     break;
  5245.                 case kCopyItem:
  5246.                     DoCopy(wind);
  5247.                     break;
  5248.                 case kPasteItem:
  5249.                     err = DoPaste(wind, (modifiers & shiftKey) != 0);
  5250.                     break;
  5251.                 case kPasteAsQuotationItem:
  5252.                     DoPaste(wind, true);
  5253.                     break;
  5254.                 case kClearItem:
  5255.                     DoClear(wind);
  5256.                     break;
  5257.                 case kSelectAllItem:
  5258.                     DoSelectAll(wind);
  5259.                     break;
  5260.                 case kFindItem:
  5261.                     err = DoFind(wind);
  5262.                     break;
  5263.                 case kFindAgainItem:
  5264.                     err = DoFindAgain(wind);
  5265.                     break;
  5266.                 case kEnterSelectionItem:
  5267.                     err = DoEnterSelection(wind);
  5268.                     break;
  5269.                 case kShowHideDetailsItem:
  5270.                     DoShowHideDetails(wind);
  5271.                     break;
  5272.                 case kRot13Item:
  5273.                     DoRot13(wind);
  5274.                     break;
  5275.                 case kInsertSpoilerCharacterItem:
  5276.                     DoInsertSpoilerCharacter(wind);
  5277.                     break;
  5278.                 case kWrapItem:
  5279.                     DoWrap(wind);
  5280.                     break;
  5281.                 case kUnwrapItem:
  5282.                     DoUnwrap(wind);
  5283.                     break;
  5284.             }
  5285.             break;
  5286.  
  5287.         case kNewsMenu:
  5288.         
  5289.             switch (item) {
  5290.                 case kSendMessageItem:
  5291.                     err = DoSendMessage(wind);
  5292.                     break;
  5293.             }
  5294.             break;
  5295.      }
  5296.      
  5297.      return err;
  5298. }
  5299.  
  5300.  
  5301.  
  5302. /*----------------------------------------------------------------------------
  5303.     Close 
  5304.     
  5305.     Close a message window.
  5306.             
  5307.     Entry:    wind = pointer to message window.
  5308.     
  5309.     Exit:    function result = error code.
  5310. ----------------------------------------------------------------------------*/
  5311.  
  5312. static OSErr Close (WindowPtr wind)
  5313. {
  5314.     TWindow **info;
  5315.     DialogPtr dlg = nil;
  5316.     short item;
  5317.     OSErr err = noErr;
  5318.  
  5319.     info = (TWindow**)GetWRefCon(wind);
  5320.  
  5321.     if ((**info).changed) {
  5322.         err = MyGetNewDialog(kAskSendOrSaveAlert, 1, 3, &dlg);
  5323.         if (err != noErr) return err;
  5324.         SetItemKeyEquivalent(dlg, 2, 'S');
  5325.         SetItemKeyEquivalent(dlg, 4, 'D');
  5326.         SysBeep(0);
  5327.         MyModalDialog(dlg, gDialogFilterUPP, &item);
  5328.         err = DoClose(dlg);
  5329.         if (err != noErr) return err;
  5330.         
  5331.         switch (item) {
  5332.             case 1: /* send */
  5333.                 err = SendMessage(wind);
  5334.                 if (err != noErr) return err;
  5335.                 break;
  5336.             case 2: /* save */
  5337.                 err = DoSave(wind, 0);
  5338.                 if (err != noErr) return err;
  5339.                 break;
  5340.             case 3: /* cancel */
  5341.                 return userCanceledErr;
  5342.             case 4: /* discard */
  5343.                 break;
  5344.         }
  5345.     }
  5346.     
  5347.     err = SaveWindPosToFile(wind);
  5348.     if (err != noErr) return err;
  5349.     
  5350.     if ((**info).theTE != nil) TEDispose((**info).theTE);
  5351.     if ((**info).tabField != nil) TEDispose((**info).tabField);
  5352.     if ((**info).quoteStringField != nil) TEDispose((**info).quoteStringField);
  5353.     if ((**info).newsgroupsField != nil) TEDispose((**info).newsgroupsField);
  5354.     if ((**info).toField != nil) TEDispose((**info).toField);
  5355.     if ((**info).subjectField != nil) TEDispose((**info).subjectField);
  5356.     if ((**info).ccField != nil) TEDispose((**info).ccField);
  5357.     if ((**info).bccField != nil) TEDispose((**info).bccField);
  5358.     if ((**info).replytoField != nil) TEDispose((**info).replytoField);
  5359.     if ((**info).followuptoField != nil) TEDispose((**info).followuptoField);
  5360.     if ((**info).keywordsField != nil) TEDispose((**info).keywordsField);
  5361.     if ((**info).distributionField != nil) TEDispose((**info).distributionField);
  5362.     if ((**info).extraNewsField != nil) TEDispose((**info).extraNewsField);
  5363.     if ((**info).extraMailField != nil) TEDispose((**info).extraMailField);
  5364.     if ((**info).signatureField != nil) TEDispose((**info).signatureField);
  5365.     MyDisposeHandle((**info).references);
  5366.     MyDisposeHandle((**info).from);
  5367.     MyDisposeHandle((**info).fields);
  5368.     MyDisposeHandle((**info).alias);
  5369.     MyDisposeHandle(info);
  5370.     
  5371.     if (gHaveDragMgr) {
  5372.         RemoveTrackingHandler(gHandleTrackingUPP, wind);
  5373.         RemoveReceiveHandler(gHandleReceiveUPP, wind);
  5374.     }
  5375.  
  5376.     MyDisposeWindow(wind);
  5377.     return noErr;
  5378. }
  5379.  
  5380.  
  5381.  
  5382. /*----------------------------------------------------------------------------
  5383.     Idle 
  5384.     
  5385.     Handle idle time tasks for a message window.
  5386.             
  5387.     Entry:    wind = pointer to message window.
  5388.     
  5389.     Exit:    cursorRgn = cursor region for WaitNextEvent mouse moved events.
  5390. ----------------------------------------------------------------------------*/
  5391.  
  5392. static void Idle (WindowPtr wind, RgnHandle cursorRgn)
  5393. {
  5394.     TWindow **info;
  5395.     short curField;
  5396.     TEHandle edit, tempEdit;
  5397.     TMsgFieldInfo **fields, *f;
  5398.     short numFields, i;
  5399.     Rect r;
  5400.     static RgnHandle rgn = nil;
  5401.     Point where;
  5402.     unsigned long fileEnabled, editEnabled, specialEnabled;
  5403.     char state;
  5404.     short selStart, selEnd;
  5405.  
  5406.     info = (TWindow**)GetWRefCon(wind);
  5407.     fields = (**info).fields;
  5408.     curField = (**info).curField;
  5409.     numFields = (**info).numFields;
  5410.     edit = (*fields)[curField].edit;
  5411.     TEIdle(edit);
  5412.     
  5413.     if (rgn == nil) rgn = NewRgn();
  5414.     SetEmptyRgn(cursorRgn);
  5415.     state = MyHGetState(fields);
  5416.     MyHLock(fields);
  5417.     for (i = 0, f = *fields; i < numFields; i++, f++) {
  5418.         tempEdit = f->edit;
  5419.         r = (**tempEdit).viewRect;
  5420.         InsetRect(&r, -kTextMargin, 0);
  5421.         LocalToGlobalRect(&r);
  5422.         RectRgn(rgn, &r);
  5423.         UnionRgn(rgn, cursorRgn, cursorRgn);    
  5424.     }
  5425.     MyHSetState(fields, state);
  5426.     if (gHaveDragMgr) SubtractTEHiliteRgn(cursorRgn, edit);
  5427.     
  5428.     GetMouse(&where);
  5429.     LocalToGlobal(&where);
  5430.     
  5431.     if (PtInRgn(where, cursorRgn)) {
  5432.         SetCursor(&gIBeamCurs);
  5433.     } else {
  5434.         SetCursor(&qd.arrow);
  5435.         ComplementRgn(cursorRgn);
  5436.     }
  5437.     
  5438.     fileEnabled = kMessageFileEnabled;
  5439.     if ((**info).alias != nil && !(**info).changed)
  5440.         fileEnabled &= ~kSaveMask;
  5441.     editEnabled = kMessageEditEnabled;
  5442.     selStart = (**edit).selStart;
  5443.     selEnd = (**edit).selEnd;
  5444.     if (selStart >= selEnd) {
  5445.         editEnabled &= ~(kCutMask | kCopyMask | kClearMask | 
  5446.             kEnterSelectionMask | kRot13Mask | kWrapMask | kUnwrapMask);
  5447.     } else if (selEnd > selStart + 255) {
  5448.         editEnabled &= ~kEnterSelectionMask;
  5449.     }
  5450.     if (MyTEGetScrapLen() == 0)
  5451.         editEnabled &= ~(kPasteMask | kPasteAsQuotationMask);
  5452.     if (edit != (**info).theTE)
  5453.         editEnabled &= ~(kPasteAsQuotationMask | kInsertSpoilerCharacterMask | 
  5454.             kRot13Mask | kWrapMask | kUnwrapMask);
  5455.     if ((**edit).teLength == 0) 
  5456.         editEnabled &= ~kSelectAllMask;
  5457.     specialEnabled = kMessageSpecialEnabled;
  5458.     if (*gFindPattern == 0) editEnabled &= ~kFindAgainMask;
  5459.     SetMenusTo(kAppleAllEnabled, fileEnabled, editEnabled, 
  5460.         kMessageNewsEnabled, specialEnabled, kMessageWindEnabled);
  5461.     SetEditMenuShowHideDetails(!(**info).showDetails);
  5462. }
  5463.  
  5464.  
  5465.  
  5466. /*----------------------------------------------------------------------------
  5467.     Help 
  5468.     
  5469.     Handle help balloons for a message window.
  5470.             
  5471.     Entry:    wind = pointer to message window.
  5472.             where = current mouse location in local coordinates.
  5473. ----------------------------------------------------------------------------*/
  5474.  
  5475. static void Help (WindowPtr wind, Point where)
  5476. {
  5477.     TWindow **info;
  5478.     short iconPanelHeight, panelHeight, index, lastIconRight, fieldIndex;
  5479.     Rect r, textRect;
  5480.     Boolean inIcon = false, checked;
  5481.     Point tip = {0, 0};
  5482.     ControlHandle sendButton, tabCheckbox, wrapCheckbox;
  5483.     TEHandle tabField, quoteStringField, field;
  5484.  
  5485.     if (DoSizeBoxAndVerticalScrollBarBalloons(wind, where)) return;
  5486.     info = (TWindow**)GetWRefCon(wind);
  5487.     iconPanelHeight = (**info).iconPanelHeight;
  5488.     panelHeight = (**info).panelHeight;
  5489.     if (where.v < iconPanelHeight) {
  5490.          if ((**info).showLabels) {
  5491.              SetRect(&r, kNewsIconH - 24, 0, 
  5492.                  kNewsIconH + 52, iconPanelHeight - 3);
  5493.          } else {
  5494.              SetRect(&r, kNewsIconH - kCheckMarkDeltaH, 0, 
  5495.                  kNewsIconH + 32, iconPanelHeight - 3);
  5496.          }
  5497.          lastIconRight = r.right + kSelfIconH - kNewsIconH;
  5498.          if (PtInRect(where, &r)) {
  5499.              inIcon = true;
  5500.              index = 46;
  5501.              checked = (**info).newsIcon;
  5502.          } else {
  5503.              OffsetRect(&r, kMailIconH - kNewsIconH, 0);
  5504.              if (PtInRect(where, &r)) {
  5505.                  inIcon = true;
  5506.                  index = 48;
  5507.                  checked = (**info).mailIcon;
  5508.              } else {
  5509.                  OffsetRect(&r, kSelfIconH - kMailIconH, 0);
  5510.                  if (PtInRect(where, &r)) {
  5511.                      inIcon = true;
  5512.                      index = 50;
  5513.                      checked = (**info).selfIcon;
  5514.                  }
  5515.              }
  5516.          }
  5517.          if (inIcon) {
  5518.              ShowHelpBalloon(tip, &r, checked ? index : index + 1);
  5519.              return;
  5520.          }
  5521.          sendButton = (**info).sendButton;
  5522.          r = (**sendButton).contrlRect;
  5523.          if (PtInRect(where, &r)) {
  5524.              ShowHelpBalloon(tip, &r, (**sendButton).contrlHilite == 0 ? 52 : 53);
  5525.              return;
  5526.          }
  5527.          SetRect(&r, lastIconRight, 0, (**sendButton).contrlRect.left, iconPanelHeight);
  5528.          if (PtInRect(where, &r)) {
  5529.              ShowHelpBalloon(tip, &r, 54);
  5530.              return;
  5531.          }
  5532.     } else if ((**info).showDetails && where.v < panelHeight) {
  5533.         tabCheckbox = (**info).tabCheckbox;
  5534.         r = (**tabCheckbox).contrlRect;
  5535.         if (PtInRect(where, &r)) {
  5536.             SetPt(&tip, r.left + 5, r.top + 8);
  5537.             ShowHelpBalloon(tip, &r, (**tabCheckbox).contrlValue == 0 ? 55 : 56);
  5538.             return;
  5539.         }
  5540.         wrapCheckbox = (**info).wrapCheckbox;
  5541.         r = (**wrapCheckbox).contrlRect;
  5542.         if (PtInRect(where, &r)) {
  5543.             SetPt(&tip, r.left + 5, r.top + 8);
  5544.             ShowHelpBalloon(tip, &r, (**wrapCheckbox).contrlValue == 0 ? 57 : 58);
  5545.             return;
  5546.         }
  5547.         tabField = (**info).tabField;
  5548.         r = (**tabField).viewRect;
  5549.         if (PtInRect(where, &r)) {
  5550.             ShowHelpBalloon(tip, &r, 59);
  5551.             return;
  5552.         }
  5553.         quoteStringField = (**info).quoteStringField;
  5554.         r = (**quoteStringField).viewRect;
  5555.         if (PtInRect(where, &r)) {
  5556.             ShowHelpBalloon(tip, &r, 60);
  5557.             return;
  5558.         }
  5559.     } else {
  5560.         field = FindFieldContainingPoint(wind, where, &fieldIndex);
  5561.         if (field != nil) {
  5562.             if (field == (**info).newsgroupsField) {
  5563.                 index = 61;
  5564.             } else if (field == (**info).toField) {
  5565.                 index = 62;
  5566.             } else if (field == (**info).subjectField) {
  5567.                 index = 63;
  5568.             } else if (field == (**info).ccField) {
  5569.                 index = 64;
  5570.             } else if (field == (**info).bccField) {
  5571.                 index = 65;
  5572.             } else if (field == (**info).replytoField) {
  5573.                 index = 66;
  5574.             } else if (field == (**info).followuptoField) {
  5575.                 index = 67;
  5576.             } else if (field == (**info).keywordsField) {
  5577.                 index = 68;
  5578.             } else if (field == (**info).distributionField) {
  5579.                 index = 69;
  5580.             } else if (field == (**info).extraNewsField) {
  5581.                 index = 70;
  5582.             } else if (field == (**info).extraMailField) {
  5583.                 index = 71;
  5584.             } else if (field == (**info).signatureField) {
  5585.                 index = 72;
  5586.             } else if (field == (**info).theTE) {
  5587.                 index = 73;
  5588.             }
  5589.             GetTextRect(wind, &textRect);
  5590.             r = (**field).viewRect;
  5591.             SectRect(&textRect, &r, &r);
  5592.             if (PtInRect(where, &r)) {
  5593.                 SetPt(&tip, r.left + 3, r.top + 3);
  5594.                 ShowHelpBalloon(tip, &r, index);
  5595.                 return;
  5596.             }
  5597.         }
  5598.     }
  5599. }
  5600.  
  5601.  
  5602.  
  5603. /*----------------------------------------------------------------------------
  5604.     InitMessageDispatchTable 
  5605.     
  5606.     Initialize the dispatch table for message windows.
  5607. ----------------------------------------------------------------------------*/
  5608.  
  5609. void InitMessageDispatchTable (void)
  5610. {
  5611.     TDispatch *d;
  5612.     
  5613.     d = &gDispatch[kMessage];
  5614.     
  5615.     d->activate = Activate;
  5616.     d->update = Update;
  5617.     d->mouse = Mouse;
  5618.     d->draggable = Draggable;
  5619.     d->key = Key;
  5620.     d->grow = Grow;
  5621.     d->zoom = Zoom;
  5622.     d->command = Command;
  5623.     d->close = Close;
  5624.     d->idle = Idle;
  5625.     d->help = Help;
  5626.     
  5627.     gAutoScrollUPP = NewTEClickLoopProc(AutoScroll);
  5628.     gHandleTrackingUPP = NewDragTrackingHandlerProc(HandleTracking);
  5629.     gHandleReceiveUPP = NewDragReceiveHandlerProc(HandleReceive);
  5630.     gScrollActionUPP = NewControlActionProc(ScrollAction);
  5631. }
  5632.